入门题,考察 x64的字符格式化漏洞 和 Canray

main里一个switch case
1 | 1.add |
menu()这也有

再看每个函数
add()

naming()

这里很明显的存在fmt漏洞,变量名上其实也给了提示
edit()

这里是一个写入,写入长度是_b所存储的长度。
gdb断点看下b所在的位置

下一个rip地址0x55555555533c+0x2d2c=0x555555558068

其实这个是在bss部分
从反编译也可以看到

show()

思路
从反编译的地址可以看出这题开了pie,还有喜闻乐见的canary。
顺便题目还给了个libc
既然考点是format,那存在fmt漏洞的也就只有naming函数,不过他写死了长度0x30,也就只能做fmt漏洞使用,触发点不在这个func里

另一个可以输入但是没有完全写死长度的是edit()


这里他是从bss部分的_b里取长度值的,泄露pie基地址+4068就可以获取_b的地址,用fmt的$n直接写就好
梳理一下
1.现在
naming()存在fmt漏洞2.题目存在
pie和canary以及需要泄露libc3.
edit的_b作为输入长度取值地址,获取pie基地址算出_b就可以fmt修改值
因为是x64的所以fmt的取值先过6个寄存器 rdi rsi rdx rcx r8 r9
测一下 这里我给了7个%p

这里不出意外的话会输出
0x7ffff7f9c643.<nil>.0x7ffff7ec44e0.0xa.<nil>.0x70252e70252e7025.0x252e70252e70252e

x64的传值顺序是
1 | rdi rsi rdx rcx r8 r9 栈 |
小tips:直接%p的话会跳过第一个rdi
这里用%num$p的还是会跳过
泄露pie
肉眼观察printf这部分寄存器和栈上有没有可以用的func

我看中了rbp后的main+133
1 | 09:0048│+008 0x7fffffffdd78 —▸ 0x555555555461 (main+113) ◂— jmp 0x555555555497 |
数了下需要%15$p就可以leak,然后- 113就可以获取func_main,再用elf.symbol["main"]减去偏移就可以获得pie的base
payload
1 | from pwn import * |

就获得了PIE的base
Canary
一样,用fmt泄露就行

这个位置的话是%13$p
1 | ··· |
输出0x33c0c0aef6d92700
libc
泄露libc的话我这里用%7$p的setbuffer - 198就可以获取到func_setbuffer的地址

payload
1 | io.sendlineafter('>>','2') |
修改_b所存储的内容
这里用pie的base + elf.symbols["b"]就可以得到b的地址
1 | b_addr = base + elf.symbols["b"] |
然后是修改,因为是x64的,p64的地址其实是整个8地址长度,超出8但不满16的像这里0x7fffffffdd50会0x00补前面的位
又因为是小端序,所以在fmt写的时候如果先输入地址,read到\x00时会结束,导致后面fmt写操作的%numc%num$lln不会被读取到。
那就fmt_write + p64(b_addr)就ok
1 | io.sendlineafter('>>','2') |
第一次接触的话可能会对这个write的payload有点疑惑%256c其实就是256个空格,%8$lln是把前面的字符数量计数,以(lln)64位十进制整数 写到第8个地址所指向的地址(也就是栈上数从头往下数第三个),因为这里$n写的话必须是要写到一个指针所指向的地址,所以我们%8的位置需要写我们刚base+b算出的b_addr
为了避免让read对我们的write不满足字节对齐8位,%256c%8$lln长度是11,需要再补满16,就是%256c%8$llnaaaaa
如下图

继续走,看写入

此时edit()的写入长度b就被我们改为了0x100
我们再去看edit(),0x58可以覆盖完rbp,rbp-0x10的位置要放canary

我们再去找俩rop用的指令
找一下pop_rdi和ret
1 | └─$ ROPgadget --binary ./book |grep "pop rdi" |
1 | └─$ ROPgadget --binary ./book --only "ret" |
pie_base+对应的偏移即可
现在我们就凑齐了
1 | 1.edit()长度使用的b所指向的0x20被改写为0x100,使得edit存在溢出 |
此时payload
1 | payload = 0x48 * b"a" + p64(canary) + p64(ret) +p64(ret)+ p64(pop_rdi) + p64(binsh_addr) + p64(system_addr) |
完整payload
1 | from pwn import* |