Artificial truth

The more you see, the less you believe.

[archives] [latest] | [homepage] | [atom/rss]

Reversing of chapro.A
Tue 25 December 2012 — download

I was looking for a nice stuff to reverse when RootBSD pointed me to e022de72cce8129bd5ac8a0675996318. I didn't search for more information about it on the net to keep the reversing fun.

$ file e022de72cce8129bd5ac8a0675996318
e022de72cce8129bd5ac8a0675996318: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped

Ho, x86-64, really ? Nice ! But stripped. This is expected for a malware. A quick look at the sections shows nothing about a known packer, but reveals some sections that should have been stripped... Was the programmer silly enough to not read strip's man and didn't use the -s option ?!

Let's check the imports:

$ rabin2 -i e022de72cce8129bd5ac8a0675996318 | cut -d" " -f7 | cut -c6- | sort
ap_add_output_filter
ap_hook_insert_filter
ap_md5
ap_pass_brigade
apr_brigade_cleanup
apr_brigade_create
apr_bucket_alloc
apr_bucket_eos_create
apr_bucket_free
apr_bucket_heap_create
apr_bucket_type_eos
ap_register_output_filter
apr_file_close
apr_file_open
apr_palloc
apr_table_add
apr_table_get
ap_set_flag_slot
ceil
close
connect
__ctype_b_loc
__ctype_tolower_loc
__ctype_toupper_loc
__cxa_finalize
fclose
fgets
fopen
__fprintf_chk
fread
gethostbyname
getpwnam
gettimeofday
__gmon_start__
gmtime
inet_ntoa
_Jv_RegisterClasses
malloc
memcpy
__memcpy_chk
memset
open
opendir
rand
read
readdir
recv
remove
rts]
send
snprintf
__snprintf_chk
socket
__sprintf_chk
srand
__stack_chk_fail
strchr
strcmp
strftime
strlen
strncpy
strspn
strstr
strtok
__strtol_internal
time
uname
__xstat

What are all those strange apr_* imports ? The Great Internet tells us Apache Portable Runtime. An Apache malware ?

Okay, let's go for symbols:

$ rabin2 -s e022de72cce8129bd5ac8a0675996318 | cut -d" " -f8 | cut -c6- | sort
_ADD_TO_BLACKLIST
_ADD_TO_WAITLIST
ARRAY_BAN_LOCAL_IP
ARRAY_BAN_PROC
ARRAY_BAN_USERAGENT
ARRAY_BLACKLIST_URI
ARRAY_SE_REFERER
ARRAY_SUDOERS
ARRAY_TAGS_FOR_INJECT
base64decode
base64encode
C_ARRAY_BAN_LOCAL_IP
C_ARRAY_BAN_PROC
C_ARRAY_BAN_USERAGENT
C_ARRAY_BLACKLIST_URI
C_ARRAY_SE_REFERER
C_ARRAY_SUDOERS
C_ARRAY_TAGS_FOR_INJECT
C_CC_HOST
C_CC_REQUEST_FORMAT
C_CC_URI
CC_HOST
CC_REQUEST_FORMAT
CC_URI
chart_proxy_module
_CHECK_BLACKLIST
_CHECK_BOT_USERAGENT
_CHECK_LOCAL_IP
_CHECK_PROC
_CHECK_RAW_COOKIE
_CHECK_REFERER_IS_HOST
_CHECK_REFERER_IS_SEO
_CHECK_SITE_ADMIN
_CHECK_SITE_KERNEL
_CHECK_UTMP
_CHECK_WAITLIST
C_KEY_COOKIE_NAME
CLIENT_IP
C_LIST_PREF
C_MARKER_LEFT
C_MARKER_RIGHT
C_MODULE_VERSION
C_STRING_1
[...]
C_STRING_34
C_TMP_DIR
explode
FILENAME_UPDATING
filesize
FILTER
_fini
from_hex
_GEN_FILENAME_BLACKLIST
GEN_FILENAME_INJECT
GEN_FILENAME_SESSION
GEN_FILENAME_WAITLIST
_init
_INJECT_DO
_INJECT_LOAD
_INJECT_SAVE
_INJECT_SKIP
_INJECT_UPDATE
ip2long
_IS_SUDOER
KEY_CLIENT
KEY_COOKIE_NAME
KEY_XOR
LIST_PREF
MARKER_LEFT
MARKER_RIGHT
max
min
MODULE_VERSION
ols]
rtrim
_SESSION_DELETE
_SESSION_KEYGEN
_SESSION_LOAD
_SESSION_SAVE
_SET_COOKIE_KEY
SIZE_ARRAY_BAN_PROC
SIZE_ARRAY_BAN_USERAGENT
SIZE_ARRAY_BLACKLIST_URI
SIZE_ARRAY_SE_REFERER
SIZE_ARRAY_SUDOERS
SIZE_ARRAY_TAGS_FOR_INJECT
STRING_1
[...]
STRING_34
stristr
TMP_DIR
to_hex
urlencode
xor_decrypt_string
xor_encrypt
xor_encrypt_string

Looks like an Apache malware ! Since some useragents/uri/referers/locals ip seems to be checked/banned, maybe the malware deliver its "gifts" only to human visitors of the website, and not to bots. What the fuck are xor_* ?! Encrypting with XOR is the same thing as decrypting... Maybe (and hopefully) these are misleading names.

Start function

/ function: section..text (23)
| 0x000041f0 4883ec08 sub rsp, 0x8
| 0x000041f4 488b05e5402000 mov rax, [rip+0x2040e5]
| 0x000041fb 4885c0 test rax, rax
| 0x000041fe 7402 jz loc.00004202
| 0x00004200 ffd0 call rax
| 0x00004202 4883c408 add rsp, 0x8
 0x00004206 c3 ret

Weird. The trick is that call rax is a call to gmon_start.

The function call_gmon_start initializes the gmon profiling system. This system is enabled when binaries are compiled with the -pg flag, and creates output for use with gprof(1)

So, we now've got the entrypoint.

Dirty hooker

Looks like 0x4330 is responsible of the hooking process. If we take a closer look to _ap_register_output_filter, we can see that the filter parameter is 0x69d0, which contains a lot of calls to _xor_decrypt_string. Ho, look at the name of the variable being pushed into rbx: KEY_XOR. This must be a joke.

Decrypting

Gogogadgeto-python !

import sys
from itertools import cycle, izip

tab = [
{'name':'C_MODULE_VERSION', 'size':10, 'offset':0x892c},
{'name':'C_CC_HOST', 'size':15, 'offset':0x8936},
{'name':'C_CC_URI', 'size':10, 'offset':0x8945},
{'name':'C_CC_REQUEST_FORMAT', 'size':96, 'offset':0x8960},
{'name':'C_MARKER_LEFT', 'size':3, 'offset':0x89c0},
{'name':'C_MARKER_RIGHT', 'size':3, 'offset':0x89c3},
{'name':'C_TMP_DIR', 'size':1, 'offset':0x89c6},
{'name':'C_LIST_PREF', 'size':5, 'offset':0x89ca},
{'name':'C_COOKIE_NAME', 'size':15, 'offset':0x89cf},
{'name':'C_ARRAY_TAGS_FOR_INJECT', 'size':77, 'offset':0x89e0},
{'name':'C_ARRAY_BAN_USERAGENT', 'size':601, 'offset':0x8a40},
{'name':'C_ARRAY_BLACKLIST_URI', 'size':5, 'offset':0x8c99},
{'name':'C_ARRAY_SE_REFERRER', 'size':281, 'offset':0x8ca0},
{'name':'C_ARRAY_SUDOERS', 'size':1, 'offset':0x8db9},
{'name':'C_ARRAY_BAN_PROC', 'size':98, 'offset':0x8dc0},
{'name':'C_ARRAY_BAN_LOCAL_IP', 'size':48, 'offset':0x8e40},
{'name':'C_STRING_1', 'size':12, 'offset':0x8e70},
{'name':'C_STRING_2', 'size':9, 'offset':0x8e7c},
{'name':'C_STRING_3', 'size':1, 'offset':0x8e85},
{'name':'C_STRING_5', 'size':21, 'offset':0x8e90},
{'name':'C_STRING_5', 'size':1, 'offset':0x8ea5},
{'name':'C_STRING_6', 'size':10, 'offset':0x8ea6},
{'name':'C_STRING_7', 'size':6, 'offset':0x8eb0},
{'name':'C_STRING_8', 'size':7, 'offset':0x8eb6},
{'name':'C_STRING_9', 'size':15, 'offset':0x8ebd},
{'name':'C_STRING_10', 'size':9, 'offset':0x8ecc},
{'name':'C_STRING_11', 'size':9, 'offset':0x8ed5},
{'name':'C_STRING_12', 'size':6, 'offset':0x8ede},
{'name':'C_STRING_13', 'size':1, 'offset':0x8ee4},
{'name':'C_STRING_14', 'size':7, 'offset':0x8ee5},
{'name':'C_STRING_15', 'size':6, 'offset':0x8eec},
{'name':'C_STRING_16', 'size':10, 'offset':0x8ef2},
{'name':'C_STRING_17', 'size':7, 'offset':0x8efc},
{'name':'C_STRING_18', 'size':1, 'offset':0x8f03},
{'name':'C_STRING_19', 'size':1, 'offset':0x8f07},
{'name':'C_STRING_20', 'size':3, 'offset':0x8f09},
{'name':'C_STRING_21', 'size':23, 'offset':0x8f10},
{'name':'C_STRING_22', 'size':10, 'offset':0x8f27},
{'name':'C_STRING_23', 'size':24, 'offset':0x8f40},
{'name':'C_STRING_24', 'size':10, 'offset':0x8f58},
{'name':'C_STRING_25', 'size':1, 'offset':0x8f62},
{'name':'C_STRING_26', 'size':1, 'offset':0x8f63},
{'name':'C_STRING_27', 'size':12, 'offset':0x8f65},
{'name':'C_STRING_28', 'size':23, 'offset':0x8f80},
{'name':'C_STRING_29', 'size':21, 'offset':0x8fa0},
{'name':'C_STRING_30', 'size':6, 'offset':0x8fb5},
{'name':'C_STRING_31', 'size':10, 'offset':0x8fbb},
{'name':'C_STRING_32', 'size':13, 'offset':0x8fc5},
{'name':'C_STRING_33', 'size':20, 'offset':0x8fe0},
{'name':'C_STRING_34', 'size':1, 'offset':0x8ff4},
]

if len(sys.argv) != 2:
    print('Usage: %s chapro.A sample') % sys.argv[0]
    sys.exit(0)

fd = open(sys.argv[1], 'r')

#the XOR key is at offset 0x8920
fd.seek(0x8920)
key = fd.read(12)

for s in tab:
    fd.seek(s['offset'])
    data = fd.read(s['size'])
    decrypted = ''.join(chr(ord(c)^ord(k)) for c,k in izip(data,
    cycle(key)))
    clear_text = decrypted.split('x00')[0]
    print('%s: %s') % (s['name'], clear_text)

Result:

C_MODULE_VERSION: 2012.08.07
C_CC_HOST: 178.162.130.105
C_CC_URI: /index.php
C_CC_REQUEST_FORMAT: POST %s HTTP/1.1
Host: %s
Content-Type: application/x-www-form-urlencoded
Content-Length: %i

<p>
%s
C_MARKER_LEFT: {{{
C_MARKER_RIGHT: }}}
C_TMP_DIR: /
C_LIST_PREF: sess_
C_COOKIE_NAME: PHP_SESSION_ID=
C_ARRAY_TAGS_FOR_INJECT:

</script>


</style>
<p>


</title>






</table>
</h1>
<p>
</i>

</ul>
C_ARRAY_BAN_USERAGENT: CHROME
GOOGLEBOT
SLURP
YAHOO
BING
LINUX
OPENBSD
[...]
CURL
PHP
INDY LIBRARY
C_ARRAY_BLACKLIST_URI: ADMIN
C_ARRAY_SE_REFERRER: GOOGLE.
YAHOO.
YANDEX.
RAMBLER.
[...]
VERDEN.
C_ARRAY_SUDOERS: r
C_ARRAY_BAN_PROC: f7277f6714e4b034216cf6558cc6327b
28878074a3dd19c7361e8a6d3f04fc17
d0415afe195478d4d8c9af205644f070
C_ARRAY_BAN_LOCAL_IP:
C_STRING_1: %i
%i
%i
%s

C_STRING_2: text/html
C_STRING_3: %
C_STRING_5: document.write('%s');
C_STRING_5: r
C_STRING_6: User-Agent
C_STRING_7: %s%.*s
C_STRING_8: Referer
C_STRING_9: X-Forwarded-For
C_STRING_10: Client-IP
C_STRING_11: X-Real-IP
C_STRING_12: Cookie
C_STRING_13: ;
C_STRING_14: %s/%s%s
C_STRING_15: INJECT
C_STRING_16: javascript
C_STRING_17: text/js
C_STRING_18: j
C_STRING_19:

C_STRING_20: id=
C_STRING_21: %a %d-%b-%Y %H:%M:%S %Z
C_STRING_22: Set-Cookie
C_STRING_23: %s%i; expires=%s; path=/
C_STRING_24: Set-Cookie
C_STRING_25: w
C_STRING_26: %
C_STRING_27: Request-Hash
C_STRING_28: c=1&version=%s&uname=%s
C_STRING_29: c=1&version=%s&uname=
C_STRING_30: /proc/
C_STRING_31: 0123456789
C_STRING_32: /proc/%s/comm
C_STRING_33: /usr/lib/libbdl.so.0
C_STRING_34: U

Woo, lots of stuffs here ! But not many interesting ones. We can see that a cookie is set, maybe for statistics, of in order to avoid spamming client. Somme googling on the md5 hashes didn't gave any results, too bad. Maybe anti-viruses names ? Who knows. Ho, looks like we've got the CC's address: 178.162.130.105. A whois tells us Frankfurt, Germany, but lit's down. We'll never know the payloads.

Blacklist

If the client matches one of the following conditions, its IP is added to the blacklist.

  1. Does the client's ip match a logged-on-the-system one (this is my favorite part)
  2. Is the url in the /admin folder ?

Papier bitte

Some checks are made before delivering the payload:

  1. Does the page contains text/html or javascript resource ?
  2. Is the useragent the one from a bot ?
  3. Is the IP in the blacklist ?
  4. Is the IP in the waitlist ?
  5. Is the cookie present ? (see below)
  6. Is check if some process are running (process's names are hashed, by I guess they are anti-viruses ones)

Once the client has successfully passed all the tests, the malware sends it the payload, sets a cookie on it, and adds its IP to the waitlist. The waitlist is misnamed (or my analyses are wrong), it's a sort of blacklist, but for infected clients. This prevent the client from being spammed by the malware.

Injection

Payloads

The inject_update function is (obviously) responsible of the payload's updating. Nothing funky here, except an ugly (please don't to this) hack: It opens a socket, fopen /usr/lib/libbdl.so.0, and use a POST request of this canvas:

POST %s HTTP/1.1
Host: %s
Content-Type: application/x-www-form-urlencoded
Content-Length: %i

%s

Then the payload is "unpacked" with a base64decode and a XOR. The same mean is used for updating the malware itself.

check_UTMP: nice trick

This function checks the content of /var/run/utmp. The manpage says

The utmp file allows one to discover information about who is currently using the system. There may be more users currently using the system, because not all programs use utmp logging.

Clever ! This allows to blacklist people logged on the machine.

Still in construction ?

I think this is a development version of the malware for the following reasons:

  • Some functions are not implemented, and always returns 1, like check_site_kernel, check_local_ip, ...
  • The xor_* collection is (hopefully) a private joke
  • Some functions are not really usefull, like is_sudoer,

Misc

The presence of a explode function may denote the fact that the author is used to php ;) A quick look with the strings command tells us that the module name is mod_chart_proxy.so. Go check you Apache server !

Conclusion

Nothing hardcore, but still entertaining to reverse: it's the first time that I see an Apache malware. It's a clever idea, but not well coded. I was pretty happy to see that ESET found the same stuffs as me, and jealous that they had access to the payload !

Resources