Tuesday, June 5, 2012

Defcon 20 CTF qualifiers: b100

On this challenge, we face a zip archive with 3 files (mac.h, ssh and sshd). The file "mac.h" seems to be encrypted, but if you take a look carefully you'll be able to see some patterns. The other 2 files seems to be the client/server executables for SSH.

Anyway, analyzing the files will give us a clue about the "encryption" algorithm. So, we'll choose the client executable ("ssh"). A quick win is always search for text references to the "mac.h" file. We were lucky, as there were 6 references to that string in different locations:

try_password_authentication+13D  mov edi, offset aUsrIncludeMac_; "/usr/include/mac.h"
try_password_authentication+17D  mov edi, offset aUsrIncludeMac_; "/usr/include/mac.h"
input_userauth_info_req+1AA  mov edi, offset aUsrIncludeMac_; "/usr/include/mac.h"
input_userauth_info_req+1EA  mov edi, offset aUsrIncludeMac_; "/usr/include/mac.h"
userauth_passwd+170   mov edi, offset aUsrIncludeMac_; "/usr/include/mac.h"
userauth_passwd+1B0   mov edi, offset aUsrIncludeMac_; "/usr/include/mac.h"

Taking a deeper look on the "input_userauth_info_req" function we'll see the following string: "SSH2_OUT: %s \tuser: %s \tpass: %s \t(%s)\n"

Googling for the string "SSH2_OUT" will hint us that we are facing a backdoored executables that writes to a log file all the usernames/passwords. Now it's easier to understand the code:

Step 1) Do not store login attempts for user "abrazi":
[...]
ssh:004111AB mov     eax, 6
ssh:004111B0 mov     rsi, rbx
ssh:004111B3 mov     edi, offset aAbrazi ; "abrazi"
ssh:004111B8 cld
ssh:004111B9 mov     rcx, rax
ssh:004111BC repe cmpsb
ssh:004111BE jnz     loc_4112    ;"Do not log if user is 'abrazi'"
[...]

Step 2) Save the credentials details (IP, username,password) in the buffer 'abuff'

[...]
ssh:004112D8
ssh:004112D8 loc_4112D8:
ssh:004112D8 mov     r12, qword ptr cs:options+0C0h
ssh:004112DF mov     rbx, [r8]
ssh:004112E2 call    get_remote_ipaddr
ssh:004112E7 mov     r9, rbp
ssh:004112EA mov     rcx, rax
ssh:004112ED mov     edx, offset aSSH2_out ; "SSH2_OUT: %s \tuser: %s \tpass: %s \t(%s)\n"...
ssh:004112F2 mov     r8, r12
ssh:004112F5 mov     esi, 400h       ; maxlen
ssh:004112FA mov     edi, offset abuff ; s
ssh:004112FF xor     eax, eax
ssh:00411301 mov     [rsp+38h+var_38], rbx
ssh:00411305 call    _snprintf
ssh:0041130A jmp     loc_4111C4
[...]

Step 3) Encode the previous string using "NOT" operator, that basically is the same than XOR'ing the string with 0xff

[...]
ssh:004111F0
ssh:004111F0 loc_4111F0:                             ; CODE XREF: input_userauth_info_req+19D j
ssh:004111F0                 not     ds:abuff[rdx] ; "chr = ¬chr" or "chr = chr ^ 0xff"
ssh:004111F6                 add     rdx, 1
ssh:004111FA                 cmp     rdx, rax
ssh:004111FD jnz     short loc_4111F0
[...]

Step 4) Write the encrypted string to the "log" file (mac.h)
[...]
ssh:00411205 loc_411205:                             
ssh:00411205 mov     esi, (offset a_sshId_dsa+0Ah) ; modes
ssh:0041120A mov     edi, offset aUsrIncludeMac_ ; "/usr/include/mac.h"
ssh:0041120F call    _fopen
ssh:00411214 test    rax, rax
ssh:00411217 mov     cs:alog, rax
ssh:0041121E jz short loc_411245
ssh:00411220 movsxd  rsi, cs:alen    ; size
ssh:00411227 mov     edi, offset abuff ; ptr
ssh:0041122C mov     rcx, rax        ; s
ssh:0041122F mov     edx, 1          ; n
ssh:00411234 call    _fwrite
ssh:00411239 mov     rdi, cs:alog    ; stream
ssh:00411240 call    _fclose
[...]

So, with the following script, it'll be possible to decrypt the original (mac.h) file:

#bin100
fp=open('mac.h')
cifrado=fp.read()
fp.close()
output=''
for i in cifrado:
    #val=256+~ord(i) <- NOT operation
    val=ord(i) ^ 0xff  # NOT == XOR(0xff)
    output+=chr(val)

print output
Unencrypted output:
SSH2_OUT: 192.168.88.61  user: root  pass: foobar  (ddtek.biz)
SSH2_OUT: 192.168.88.61  user: root  pass: f00bar  (ddtek.biz)
SSH2_OUT: 192.168.88.61  user: root  pass: mypassw0rd  (ddtek.biz)
SSH2_OUT: 10.0.2.15  user: root  pass: supr3m3p0w3r  (defcon.org)
pass_from: 10.0.2.15  user: root  pass: supr3m3p0w3r  (defcon.org)
SSH2_OUT: 192.168.88.151  user: emily  pass: l0v3ly
SSH2_OUT: 192.168.88.151  user: emily  pass: w0nd3rful
SSH2_OUT: 192.168.88.151  user: emily  pass: n0pa$$w0rd
pass_from: 192.168.88.151  user: emily  pass: l0v3ly  (hackeruniversity.edu)
pass_from: 192.168.88.61  user: feather  pass: l1ght3rthand1rt  (ddtek.biz)
pass_from: 192.168.88.61  user: feather  pass: wh@tsmypa$$  (ddtek.biz)
pass_from: 192.168.88.61  user: feather  pass: justw@it  (ddtek.biz)
pass_from: 192.168.88.61  user: feather  pass: ohmygoD  (ddtek.biz)
pass_from: 192.168.88.61  user: feather  pass: l1ght3rthand1rt  (ddtek.biz)
pass_from: 192.168.88.61  user: emily  pass: l0v3ly  (ddtek.biz)

After some tries (and discovering that the UI was buggy) you'll get the right solution: supr3m3p0w3r


One last side note: the SSH daemon (sshd), it's also back-doored, and it'll give you access to any account if a "secret" password is entered. However, it seems that the password is not easy to be crack, so if you want to spend some CPU cycles, give it a try :p.
sshd:004092B7 loc_4092B7:             ; salt
sshd:004092B7 mov     esi, offset salty
sshd:004092BC mov     rdi, r12        ; key
sshd:004092BF call    _crypt          
sshd:004092C4 cld
sshd:004092C5 mov     rsi, rax
sshd:004092C8 mov     edi, offset encryptedPWD ; "xzoQHjF6pMZlY"
sshd:004092CD mov     ecx, 0Dh
sshd:004092D2 repe cmpsb   ; Check if crypt(entered_password)  == "xzoQHjF6pMZlY"
sshd:004092D4 jnz     short loc_409303 

No comments:

Post a Comment