Write-up - Insomni'hack 2011 - Reverse 2

by Jonathan Salwan - 2011-03-06

First, we check the file type

jonathan@ArchLinux [reverse] $ file server
server: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for
GNU/Linux 2.6.15, not stripped

Ok, it's classical linux/x86-32 bits binary.

Then, we create a file called key.txt for the same context than the CTF.

jonathan@ArchLinux [reverse] $ echo "Bravo, le flag est: xxxxx" >> key.txt
jonathan@ArchLinux [reverse] $ cat key.txt
Bravo, le flag est: xxxxx
jonathan@ArchLinux [reverse] $

The binary calls the net_init function which initializes the socket. We reverse it for know what is the port listened.

gdb$ disass net_init
Dump of assembler code for function net_init:
[...]
0x08048aea <+138>:   movw   $0x2,-0x1c(%ebp)
0x08048af0 <+144>:   mov    0x8(%ebp),%eax
0x08048af3 <+147>:   movzwl %ax,%eax
0x08048af6 <+150>:   mov    %eax,(%esp)
0x08048af9 <+153>:   call   0x80487e8 <htons@plt>
0x08048afe <+158>:   mov    %ax,-0x1a(%ebp)
0x08048b02 <+162>:   movl   $0x0,-0x18(%ebp)
0x08048b09 <+169>:   movl   $0x8,0x8(%esp)
0x08048b11 <+177>:   movl   $0x0,0x4(%esp)
[...]
gdb$ b *0x08048af9
Breakpoint 1 at 0x8048af9
gdb$ r
[Thread debugging using libthread_db enabled]
--------------------------------------------------------------------------[regs]
EAX: 000022B8  EBX: B7F9CFF4  ECX: 00000004
EDX: 00000802  ESI: 00000000  EDI: 00000000
EBP: BFFFF7B8  ESP: BFFFF770  EIP: 08048AF9

--------------------------------------------------------------------------[code]
=> 0x8048af9 <net_init+153>:    call   0x80487e8 <htons@plt>
   0x8048afe <net_init+158>:    mov    %ax,-0x1a(%ebp)
   0x8048b02 <net_init+162>:    movl   $0x0,-0x18(%ebp)
   0x8048b09 <net_init+169>:    movl   $0x8,0x8(%esp)
   0x8048b11 <net_init+177>:    movl   $0x0,0x4(%esp)
   0x8048b19 <net_init+185>:    lea    -0x1c(%ebp),%eax
   0x8048b1c <net_init+188>:    add    $0x8,%eax
   0x8048b1f <net_init+191>:    mov    %eax,(%esp)
--------------------------------------------------------------------------------

Breakpoint 1, 0x08048af9 in net_init ()
gdb$

EAX = 0x22b8. So, the port is 8888.

Another interesting function, talk_with_client. Let's reverse it.

gdb$ disass talk_with_client
Dump of assembler code for function talk_with_client:
0x08048e93 <+0>:    push   %ebp
0x08048e94 <+1>:    mov    %esp,%ebp
0x08048e96 <+3>:    sub    $0x38,%esp
0x08048e99 <+6>:    movl   $0x0,-0x10(%ebp)
0x08048ea0 <+13>:   movl   $0x0,-0x18(%ebp)
0x08048ea7 <+20>:   movl   $0x0,-0x1c(%ebp)
0x08048eae <+27>:   mov    0x8(%ebp),%eax
0x08048eb1 <+30>:   mov    (%eax),%eax
0x08048eb3 <+32>:   mov    %eax,-0xc(%ebp)
0x08048eb6 <+35>:   movl   $0x9,(%esp)
0x08048ebd <+42>:   call   0x80488b8 <malloc@plt>
0x08048ec2 <+47>:   mov    %eax,-0x1c(%ebp)
0x08048ec5 <+50>:   movl   $0x9,0x8(%esp)
0x08048ecd <+58>:   mov    -0x1c(%ebp),%eax
0x08048ed0 <+61>:   mov    %eax,0x4(%esp)
0x08048ed4 <+65>:   mov    -0xc(%ebp),%eax
0x08048ed7 <+68>:   mov    %eax,(%esp)
0x08048eda <+71>:   call   0x80487f8 <read@plt>
0x08048edf <+76>:   mov    %eax,-0x14(%ebp)
0x08048ee2 <+79>:   cmpl   $0x9,-0x14(%ebp)
0x08048ee6 <+83>:   je     0x8048efe <talk_with_client+107>
0x08048ee8 <+85>:   mov    $0x80490cf,%eax
0x08048eed <+90>:   mov    -0x14(%ebp),%edx
0x08048ef0 <+93>:   mov    %edx,0x4(%esp)
0x08048ef4 <+97>:   mov    %eax,(%esp)
0x08048ef7 <+100>:  call   0x8048878 <printf@plt>
0x08048efc <+105>:  jmp    0x8048f65 <talk_with_client+210>
0x08048efe <+107>:  mov    -0x1c(%ebp),%eax
0x08048f01 <+110>:  mov    %eax,(%esp)
0x08048f04 <+113>:  call   0x8048e27 <validate_input>
0x08048f09 <+118>:  mov    %eax,-0x10(%ebp)
0x08048f0c <+121>:  cmpl   $0x0,-0x10(%ebp)
0x08048f10 <+125>:  je     0x8048f4f <talk_with_client+188>
0x08048f12 <+127>:  call   0x8048d58 <read_key>
0x08048f17 <+132>:  mov    %eax,-0x18(%ebp)
0x08048f1a <+135>:  cmpl   $0x0,-0x18(%ebp)
0x08048f1e <+139>:  je     0x8048f4f <talk_with_client+188>
0x08048f20 <+141>:  mov    -0x18(%ebp),%eax
0x08048f23 <+144>:  mov    %eax,(%esp)
0x08048f26 <+147>:  call   0x8048858 <strlen@plt>
0x08048f2b <+152>:  add    $0x1,%eax
0x08048f2e <+155>:  mov    %eax,0x8(%esp)
0x08048f32 <+159>:  mov    -0x18(%ebp),%eax
0x08048f35 <+162>:  mov    %eax,0x4(%esp)
0x08048f39 <+166>:  mov    -0xc(%ebp),%eax
0x08048f3c <+169>:  mov    %eax,(%esp)
0x08048f3f <+172>:  call   0x80487a8 <write@plt>
0x08048f44 <+177>:  mov    -0x18(%ebp),%eax
0x08048f47 <+180>:  mov    %eax,(%esp)
0x08048f4a <+183>:  call   0x8048808 <free@plt>
0x08048f4f <+188>:  mov    -0x1c(%ebp),%eax
0x08048f52 <+191>:  mov    %eax,(%esp)
0x08048f55 <+194>:  call   0x8048808 <free@plt>
0x08048f5a <+199>:  mov    -0xc(%ebp),%eax
0x08048f5d <+202>:  mov    %eax,(%esp)
0x08048f60 <+205>:  call   0x80488a8 <close@plt>
0x08048f65 <+210>:  leave
0x08048f66 <+211>:  ret
End of assembler dump.
gdb$

This function calls two interesting functions:

  • validate_input
  • read_key

Let's reverse validate_input.

gdb$ disass validate_input
Dump of assembler code for function validate_input:
0x08048e27 <+0>:    push   %ebp
0x08048e28 <+1>:    mov    %esp,%ebp
0x08048e2a <+3>:    sub    $0x10,%esp
0x08048e2d <+6>:    mov    0x8(%ebp),%eax
0x08048e30 <+9>:    movzbl (%eax),%eax
0x08048e33 <+12>:   cmp    $0x53,%al
0x08048e35 <+14>:   je     0x8048e3e <validate_input+23>
0x08048e37 <+16>:   mov    $0x0,%eax
0x08048e3c <+21>:   jmp    0x8048e91 <validate_input+106>
0x08048e3e <+23>:   movl   $0x0,-0x4(%ebp)
0x08048e45 <+30>:   jmp    0x8048e86 <validate_input+95>
0x08048e47 <+32>:   mov    -0x4(%ebp),%eax
0x08048e4a <+35>:   add    0x8(%ebp),%eax
0x08048e4d <+38>:   movzbl (%eax),%eax
0x08048e50 <+41>:   movsbl %al,%eax
0x08048e53 <+44>:   movzbl %al,%ecx
0x08048e56 <+47>:   mov    -0x4(%ebp),%eax
0x08048e59 <+50>:   add    $0x1,%eax
0x08048e5c <+53>:   add    0x8(%ebp),%eax
0x08048e5f <+56>:   movzbl (%eax),%eax
0x08048e62 <+59>:   movsbl %al,%edx
0x08048e65 <+62>:   mov    -0x4(%ebp),%eax
0x08048e68 <+65>:   add    $0x2,%eax
0x08048e6b <+68>:   add    0x8(%ebp),%eax
0x08048e6e <+71>:   movzbl (%eax),%eax
0x08048e71 <+74>:   movsbl %al,%eax
0x08048e74 <+77>:   lea    (%edx,%eax,1),%eax
0x08048e77 <+80>:   cmp    %eax,%ecx
0x08048e79 <+82>:   je     0x8048e82 <validate_input+91>
0x08048e7b <+84>:   mov    $0x0,%eax
0x08048e80 <+89>:   jmp    0x8048e91 <validate_input+106>
0x08048e82 <+91>:   addl   $0x3,-0x4(%ebp)
0x08048e86 <+95>:   cmpl   $0x8,-0x4(%ebp)
0x08048e8a <+99>:   jle    0x8048e47 <validate_input+32>
0x08048e8c <+101>:  mov    $0x1,%eax
0x08048e91 <+106>:  leave
0x08048e92 <+107>:  ret
End of assembler dump.
gdb$

Let's reverse it, step by step.

0x08048e2d <+6>:    mov    0x8(%ebp),%eax
0x08048e30 <+9>:    movzbl (%eax),%eax
0x08048e33 <+12>:   cmp    $0x53,%al

The function compares the first character by the 0x53 (S)

0x08048e37 <+16>:    mov    $0x0,%eax
0x08048e3c <+21>:    jmp    0x8048e91 <validate_input+106>

If the first character isn't S we leave this function and return 0.

0x08048e3e <+23>:    movl   $0x0,-0x4(%ebp)
0x08048e45 <+30>:    jmp    0x8048e86 <validate_input+95>

It initializes the i variable with value 0.

0x08048e86 <+95>:    cmpl   $0x8,-0x4(%ebp)
0x08048e8a <+99>:    jle    0x8048e47 <validate_input+32>

And compares if the size of string is 0x08. So, the password has 9 characters.

0x08048e47 <+32>:    mov    -0x4(%ebp),%eax
0x08048e4a <+35>:    add    0x8(%ebp),%eax
0x08048e4d <+38>:    movzbl (%eax),%eax
0x08048e50 <+41>:    movsbl %al,%eax
0x08048e53 <+44>:    movzbl %al,%ecx
0x08048e56 <+47>:    mov    -0x4(%ebp),%eax
0x08048e59 <+50>:    add    $0x1,%eax
0x08048e5c <+53>:    add    0x8(%ebp),%eax
0x08048e5f <+56>:    movzbl (%eax),%eax
0x08048e62 <+59>:    movsbl %al,%edx
0x08048e65 <+62>:    mov    -0x4(%ebp),%eax
0x08048e68 <+65>:    add    $0x2,%eax
0x08048e6b <+68>:    add    0x8(%ebp),%eax
0x08048e6e <+71>:    movzbl (%eax),%eax
0x08048e71 <+74>:    movsbl %al,%eax
0x08048e74 <+77>:    lea    (%edx,%eax,1),%eax
0x08048e77 <+80>:    cmp    %eax,%ecx
0x08048e79 <+82>:    je     0x8048e82 <validate_input+91>
0x08048e7b <+84>:    mov    $0x0,%eax
0x08048e80 <+89>:    jmp    0x8048e91 <validate_input+106>
0x08048e82 <+91>:    addl   $0x3,-0x4(%ebp)
0x08048e86 <+95>:    cmpl   $0x8,-0x4(%ebp)
0x08048e8a <+99>:    jle    0x8048e47 <validate_input+32>

This section adds the value of seconds character with the third character and comapres if it's equal to 0x53 ('S'). And repeats this scenario while i != 8.

So, our key is "S!2S!2S!2".

'!' + '2' = 0x21 + 0x32 = 0x53 = S

Now, let's test with the server.

jonathan@ArchLinux [write-up] $ cat connection.py
#!/usr/bin/env python2

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
answer = s.recv(1024)
print answer
connect = s.connect(('192.168.0.3', 8888))
s.send("S!2S!2S!2")
s.close
jonathan@ArchLinux [write-up] $ ./connection.py
Bravo, le flag est: xxxxx

jonathan@ArchLinux [write-up] $