CTF集训(6)
PWN(2)

工具介绍
IDA
IDA是一个反编译软件。
按住F5可以将汇编反编译成C语言。
右键copy to assembly可以查看C语言对应的汇编代码。
同时,IDA可以保存工程文件。
按住shift+F12,可以列出所有可见字符,双击可以对应的数据节。旁边会标出数据出现的位置,这样就可以顺藤摸瓜找到对应函数。
pwntools
一般来说,需要把python2和3版本都安装。
1 | from pwn import * |
连接本地程序
1 | io = process("./a") |
连接远程程序
1 | io = remote("localhost", 123) |
io是一个进程管道的实例。
接受一行
1 | io.recvline() |
发送
1 | io.send() |
发送一行,自动加换行符
1 | io.sendline() |
pwndbg
后述。
ret2text
如果能控制EIP寄存器,就能覆盖返回的指令地址。对于栈帧结构,我们如果能覆盖return address,就能控制返回地址。
1 | int main() { |
那么多余数据就会影响关键结构,导致崩溃。

以例题一为例考察。
先checksec查看类型,是32位小端序并且没有保护。
接下来直接IDA反汇编。关注到这个函数:
1 | int vulnerable() |
buffer的位置是esp+8,ebp-16。ebp一定指向栈底的return函数。一般直接看ebp的位置就可以确定栈溢出的位置。但是这样确定往往不够精确,我们不妨利用gdb。
1 | b(reakpoint) main 在main函数打断点,也可以用地址 |
具体到程序,我们看看运行时候栈的状态:
1 | 00:0000│ esp 0xffffd010 —▸ 0xf7faf000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1e9d6c |
现在输入AAAAAAAA,再来看看:
1 | 00:0000│ esp 0xffffd010 —▸ 0xf7faf000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1e9d6c |
在IDA中运行发现,有一个后门函数getshell。这样我们写16个A,然后写4个B覆盖掉EBP,最后写getshell的地址。
1 | get_shell .text 08048522 0000002F 0000000C 00000000 R . . . B T . |
那么起始地址就是8048522
这样我们找到了payload,然后开始攻击。
1 | from pwn import * |
就找到了shell.
ret2shellcode
一般来说,方便的后门程序并非时时刻刻存在,常把shellcode写入BSS区或者栈区。
由于the NO-eXecute Bytes(NX)的默认存在,栈内函数往往难以被调用。
另一个保护措施是ASLR(Address Space Layout Randomization),即共享库、栈、堆是否随机化。如果这个开关打开,那么栈和动态链接库的地址会随机进行偏移。其位于/proc/sys/kernel/randomize_va_space 。
二者导致,栈安全措施更加周到。
pwntools中有shellcraft模块。shellcraft.sh() 就得到了调用的汇编代码,asm会转换为机器码。就得到了shellcode,直接io.send即可。如果攻击64位,则使用shellcraft.amd64.sh()。一般加一句context.arch = "amd64"
看看例题。先checksec,发现有一段RWX,也就是可读可写可执行的区域。进行pwndbg,利用vmmap可以看到此时进程情况,也就是说这段区域内,程序执行者可以执行任何代码。
观察到,主函数有一个未初始化的buf2字符串,双击发现是一个100的全局变量。由于远端服务器ASLR的存在,导致我们难以用栈,所以只能用BSD。
进行分析:
1 | 07:001c│ eax 0xffffcfcc ◂— 'AAAAAAAA' |
那么距离就是0xffffd038-0xffffcfcc=108。
利用IDA分析,我们发现有一个buf2在BSD,地址是0804A080 。
接下来我们写脚本:
1 | from pwn import * |
另一个比较简单的例子是靶场上的shellcode:
1 |
|
这个直接注入进去对应的机器码执行即可。
1 | from pwn import * |




