New issue
Advanced search Search tips

Issue 283 attachment: find_ptes.py (2.9 KB)

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

import os
import struct
import sys

# This script analyses the memory dumps produced by qemu_runner.py, which
# runs privesc.cc in a QEMU VM. It estimates what proportion of bit flips
# in page table entries (PTEs) would be exploitable.


page_size = 0x1000


# Returns the index of the highest set bit, plus 1.
def HighestBit(val):
assert val >= 0
bit = 0
while val != 0:
bit += 1
val >>= 1
return bit


def Main():
filename = 'out/memory_dump'
fh = open(filename)
pfn_count = os.stat(filename).st_size / page_size
print 'Memory dump size: %i MB' % (os.stat(filename).st_size >> 20)
print 'PFN count:', pfn_count

def ReadBytes(offset, size):
fh.seek(offset)
return fh.read(size)

def GetPtes(pfn):
return struct.unpack('512Q', ReadBytes(pfn * page_size, page_size))

# Locate the data pages via the markers they contain. Look for the
# markers written by privesc.cc.
data_pages_by_pfn = {}
data_pages_count = 0
marker = struct.pack('Q', 0x43215678)
for pfn in xrange(pfn_count):
if ReadBytes(pfn * page_size, 8) == marker:
val = struct.unpack('Q', ReadBytes(pfn * page_size + 8, 8))[0]
data_pages_by_pfn[pfn] = val
data_pages_count = max(data_pages_count, val + 1)
print 'Found data pages:', data_pages_count
assert len(data_pages_by_pfn) == data_pages_count

# Locate the page tables via their characteristic contents.
expected_pte = dict((idx, (1 << 63) | (pfn << 12))
for pfn, idx in data_pages_by_pfn.iteritems())
mask = ((1 << 64) - 1) & ~0xfff
target_pts = {}
for pfn in xrange(pfn_count):
vals = GetPtes(pfn)
# Since we're using fast mode, only check the first entry.
for i in xrange(data_pages_count / 512):
if vals[0] & mask == expected_pte[i * 512]:
target_pts[pfn] = i

print '\nDiagram of page types in physical memory:'
for pfn in xrange(pfn_count):
ch = '.'
if pfn in data_pages_by_pfn:
ch = 'D'
elif pfn in target_pts:
ch = str(target_pts[pfn])
sys.stdout.write(ch)
if (pfn + 1) % 64 == 0:
sys.stdout.write(' %i MB\n' % (pfn >> (20 - 12)))

# Count how many bit flips cause PTEs to point to page tables.
print '\nBit flip analysis:'
total_hits_pts = 0
total_hits_data = 0
for bit in xrange(HighestBit(pfn_count)):
hits_pts = 0
hits_data = 0
for data_pfn in data_pages_by_pfn.iterkeys():
new_pfn = data_pfn ^ (1 << bit)
if new_pfn in target_pts:
hits_pts += 1
total_hits_pts += 1
elif new_pfn in data_pages_by_pfn:
hits_data += 1
total_hits_data += 1
print 'bit %2i: %3i PT hits (%4.1f%%) (and %3i data page hits) out of %i' % (
bit, hits_pts,
float(hits_pts) / data_pages_count * 100,
hits_data, data_pages_count)
print 'total hits for page tables (exploitable): %i' % total_hits_pts
print 'total hits for data pages (unexploitable): %i' % total_hits_data

print 'page table pages: %i' % len(target_pts)


Main()