- status: updated.
CGFsb
前面的那一道get_shell的题算是做pwn题的一般流程:下载文件,ida查看代码,分析漏洞,利用漏洞写出exp,最常用的是用到python的pwntools,然后使用nc或者pwntools连接到虚拟场景实现漏洞攻击得到flag。
本题的思路基本一致,使用pwntools编写漏洞利用脚本,这里的漏洞是格式化字符串漏洞,文章分享:https://www.cnblogs.com/ichunqiu/p/9329387.html。
ida分析,只有当pwnme这个变量的值为8的时候才可以输出flag:
pwnme这个变量处于.bss段,是一个全局变量。
而比较的前面调用了printf函数,可以看到参数就只有一个即就是你留下的message的地址:
所以可以通过格式化字符串漏洞使用%n格式修改任意地址内容,%n是一个不经常用到的格式符,其作用是把前面已经打印的长度写入某个内存地址中去。
使用ida查看call printf的地址:
使用gdb在该地址处设置断点,输入任意数据,确定message最后存入的是栈中的第几个参数:
由上图可知,输入的message“aaaa”存放在栈中第11(编写脚本的时候从0开始,所以应该是10 )个参数处。
使用ida查看pwnme变量的地址:
编写脚本,(并不是完全独立编写出来的):
remote函数是连接到远程服务器进行漏洞利用,recvuntil函数顾名思义就是等待服务器返回参数中的字符串后再执行后续操作,sendline就是向服务器发送一行数据,也就是我们的payload,最后打印出返回来的结果。
脚本执行情况:
至于为什么要打印两次,是因为成功后会输出两次数据,一次是提示成功,一次是flag:
关于评论区的问题
很多人都在问这道题的exp是怎么构造的,大伙有点不懂,说实话,我当初做的时候也是有点似懂非懂,不过现在我应该可以给你们讲清楚了,果然格式化字符串要比栈溢出难。
这道题的关键是要修改全局变量pwnme的内容,而从CTF-wiki上总结的格式化字符串我们可以知道:
%n$
:表示的是获取格式化字符串中的指定参数,n表示第几个,上面的exp中就是指10$
,即获取栈上的第11个参数。
%n
:不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
另外还有wiki百科上面的解释:
n是用这个格式说明符(specifier)显示第几个参数;这使得参数可以输出多次,使用多个格式说明符,以不同的顺序输出。 如果任意一个占位符使用了parameter,则其他所有占位符必须也使用parameter。这是POSIX扩展,不属于ISO C。例:
printf("%2$d %2$#x; %1$d %1$#x",16,17)
产生"17 0x11; 16 0x10"
再来看一下调试的结果:
gdb-peda$ r
Starting program: /home/pwn/Documents/0110/5982010c172744c8a1c93c24b5200b21
please tell me your name:
abcd
leave your message please:
aaaa
hello abcd
your message is:
[----------------------------------registers-----------------------------------]
EAX: 0xffffce68 ("aaaa\n")
EBX: 0xffffce68 ("aaaa\n")
ECX: 0xffffffff
EDX: 0xf7fb7870 --> 0x0
ESI: 0xf7fb6000 --> 0x1b1db0
EDI: 0xffffcecc --> 0x59bf0a00
EBP: 0xffffcee8 --> 0x0
ESP: 0xffffce40 --> 0xffffce68 ("aaaa\n")
EIP: 0x80486cd (<main+256>: call 0x8048460 <printf@plt>)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x80486c1 <main+244>: call 0x8048490 <puts@plt>
0x80486c6 <main+249>: lea eax,[esp+0x28]
0x80486ca <main+253>: mov DWORD PTR [esp],eax
=> 0x80486cd <main+256>: call 0x8048460 <printf@plt>
0x80486d2 <main+261>: mov eax,ds:0x804a068
0x80486d7 <main+266>: cmp eax,0x8
0x80486da <main+269>: jne 0x80486f6 <main+297>
0x80486dc <main+271>: mov DWORD PTR [esp],0x8048810
Guessed arguments:
arg[0]: 0xffffce68 ("aaaa\n")
[------------------------------------stack-------------------------------------]
0000| 0xffffce40 --> 0xffffce68 ("aaaa\n")
0004| 0xffffce44 --> 0xffffce5e ("abcd\n")
0008| 0xffffce48 --> 0xf7fb65a0 --> 0xfbad20