Back

--------------------------

FreeBSD assembly

--------------------------

...check it out on 64bit!

Hey, there kidz! Small zine on how to write assembly code "backwards" ... that is, you disassemble the C code, and from there, you try to solve the puzzle. Reasonz behind this: I have fully "migrated" to *BSD systems. *w00p-w00p* ...because reasonz! l3l! Although there is still some Linux left for future zines, my mind and 'raison d'ĂȘtre' will stay focused on FreeBSD and NetBSD, mostly. Obviously, this zine's code/command lines/tools is compatible with FreeBSD system ... it can be adapted to Linux, tho! -------------------] Prepare thy Environment Guess you will need a bit of "research" on youtube/web on how to install FreeBSD on a virtual machine/laptop. For now, no FreeBSD jails. ...for now! *evil_grin* Anywayz, my 'wrenchy' host has the following specs: root@wrenchy:/home/testish # uname -s -m -ri -n FreeBSD wrenchy 11.2-STABLE amd64 GENERIC Guess that'll do! -------------------] Gump all the OS-es! Let's learn how to salute Gump in FreeBSD. root@wrenchy:~ # man 2 write ... #include <unistd.h> ssize_t write(int fd, const void *buf, size_t nbytes); ... fd (file descriptor - STDOUT (1) ) to write to pointer to a string, and the size of that string. Now, let's check the value for the fd - file descriptor - STDOUT: root@wrenchy:/home/testish # more /usr/include/unistd.h | grep -i _fileno #define STDIN_FILENO 0 /* standard input file descriptor */ #define STDOUT_FILENO 1 /* standard output file descriptor */ #define STDERR_FILENO 2 /* standard error file descriptor */ root@wrenchy:/home/testish # Hmm.. and what else do we need? Oh, syscalls! ...in order to write: root@wrenchy:/home/testish # more /usr/src/sys/kern/syscalls.master | grep 'AUE_WRITE\>' | awk '{print $1}' 4 root@wrenchy:/home/testish # ...and exit: root@wrenchy:/home/testish # more /usr/src/sys/kern/syscalls.master | grep 'AUE_EXIT\>' | awk '{print $1}' 1 root@wrenchy:/home/testish # ...now, it's time to say hello: root@wrenchy:/home/testish # root@wrenchy:/home/testish # printf 'main(){write(1, "Hiya,\ Gump!\\n",12 ) ; }\n' | gcc -xc - -o 33 root@wrenchy:/home/testish # root@wrenchy:/home/testish # ./33 Hiya, Gump! root@wrenchy:/home/testish # -------------------] Find your way back home! Now that we have a small C code, it's time to disassemble it. Although there are known tools from Linux world (gdb, objdump), one must know that strace is replaced by truss around these parts. I trust you completely when you say you have read all the tutorials I have provided about gdb, and that you recall a bit the notions about x64_86 assembly. Let's fire up a gdb session with a small script: root@wrenchy:/home/testish # vi hehe.gdb set width 0 set height 0 set verbose off set logging on break main run disas i r rax rsi rdx rdi Let's run it to obtain the status of the rax, rsi, rdx, rdi registers: root@wrenchy:/home/testish # gdb -q --command=hehe.gdb --args ./33 (no debugging symbols found)...Breakpoint 1 at 0x4007c9 (no debugging symbols found)...(no debugging symbols found)... Breakpoint 1, 0x00000000004007c9 in main () Dump of assembler code for function main: 0x00000000004007c5 <main+0>: push %rbp 0x00000000004007c6 <main+1>: mov %rsp,%rbp 0x00000000004007c9 <main+4>: mov $0xc,%edx 0x00000000004007ce <main+9>: mov $0x400843,%esi 0x00000000004007d3 <main+14>: mov $0x1,%edi 0x00000000004007d8 <main+19>: mov $0x0,%eax 0x00000000004007dd <main+24>: callq 0x400450 <write@plt> 0x00000000004007e2 <main+29>: mov $0x0,%eax 0x00000000004007e7 <main+34>: pop %rbp 0x00000000004007e8 <main+35>: retq 0x00000000004007e9 <main+36>: nopl 0x0(%rax) End of assembler dump. rax 0x600970 6293872 rsi 0x7fffffffeb88 140737488350088 rdx 0x7fffffffeb98 140737488350104 rdi 0x1 1 (gdb) # still opened (gdb) The session is left open, so we can continue our inspection. Check again how registers rax, rsi rdx and rdi look like: (you'll keep running this...trust me!) (gdb) i r rax rsi rdx rdi rax 0x600970 6293872 rsi 0x7fffffffeb88 140737488350088 rdx 0x7fffffffeb98 140737488350104 rdi 0x1 1 (gdb) If you take a closer look at the dissambled part of the main, you will notice the following line: 0x00000000004007dd <main+24>: callq 0x400450 <write@plt> That looks nasty...eh! [[ start the extra details here ]] So what is that write@plt? That plt (Procedure Linkage Table) is just an indirection - our code does not call an external function directly, but inderectly with plt's help. So, let's find the dependencies: root@wrenchy:/home/testish # ldd 33 33: libc.so.7 => /lib/libc.so.7 (0x800822000) Now, look for the entry that we are interested about (__sys_write) root@wrenchy:/home/testish # readelf -a /lib/libc.so.7 | grep "__sys_write\>" 00000039a618 07dc00000001 R_X86_64_64 000000000015a500 __sys_write + 0 2012: 000000000015a500 17 FUNC GLOBAL DEFAULT 11 __sys_write@@FBSDprivate_1.0 (8) root@wrenchy:/home/testish # Good, starts at address 0x015a500 ... and dissasemble it: root@wrenchy:/home/testish # objdump -d /lib/libc.so.7 --start-address=0x15a500 /lib/libc.so.7: file format elf64-x86-64-freebsd Disassembly of section .init: Disassembly of section .plt: Disassembly of section .text: 000000000015a500 <__sys_write>: 15a500: b8 04 00 00 00 mov $0x4,%eax 15a505: 49 89 ca mov %rcx,%r10 15a508: 0f 05 syscall 15a50a: 0f 82 c4 00 00 00 jb 15a5d4 <__sys_ioctl+0x14> [...] So, this is how our write() is implemented ... Noice! Now, time go back to our gdb session and check for more details: [[ end the extra details here ]] Check/Break at write@plt address: (gdb) break *main+24 Breakpoint 2 at 0x4007dd (gdb) si 0x00000000004007ce in main () Let's check our registers now: (gdb) i r rax rsi rdx rdi rax 0x600970 6293872 rsi 0x7fffffffeb88 140737488350088 rdx 0xc 12 rdi 0x1 1 Continue and disassemble: (gdb) c Continuing. Breakpoint 2, 0x0000000000400450 in write@plt () (gdb) disas Dump of assembler code for function write@plt: 0x0000000000400450 <write@plt+0>: jmpq *0x20069a(%rip) # 0x600af0 <_GLOBAL_OFFSET_TABLE_+32> 0x0000000000400456 <write@plt+6>: pushq $0x1 0x000000000040045b <write@plt+11>: jmpq 0x400430 <.plt> End of assembler dump. ...and let's check again our registers: (gdb) i r rax rsi rdx rdi rax 0x0 0 rsi 0x400843 4196419 rdx 0xc 12 rdi 0x1 1 Hey, we are not done, yet: (gdb) break *0x400430 Breakpoint 3 at 0x400430 (gdb) c Continuing. Breakpoint 3, 0x0000000000400430 in .plt () (gdb) disas Dump of assembler code for function .plt: 0x0000000000400430 <.plt+0>: pushq 0x2006a2(%rip) # 0x600ad8 <_GLOBAL_OFFSET_TABLE_+8> 0x0000000000400436 <.plt+6>: jmpq *0x2006a4(%rip) # 0x600ae0 <_GLOBAL_OFFSET_TABLE_+16> 0x000000000040043c <.plt+12>: nopl 0x0(%rax) End of assembler dump. (gdb) si (gdb) # registers again (gdb) i r rax rsi rdx rdi rax 0x0 0 rsi 0x400843 4196419 rdx 0xc 12 rdi 0x1 1 ...and let's continue: (gdb) c Continuing. Hiya, Gump! Breakpoint 3, 0x0000000000400430 in .plt () Yiss! Now, check again registers: (gdb) i r rax rsi rdx rdi rax 0x0 0 rsi 0x400843 4196419 rdx 0xc 12 rdi 0x0 0 (gdb) [[From retq address ]] (gdb) break *main+35 Breakpoint 2 at 0x4007e8 (gdb) si 0x00000000004007ce in main () (gdb) i r rax rsi rdx rdi rax 0x600970 6293872 rsi 0x7fffffffeb88 140737488350088 rdx 0xc 12 rdi 0x1 1 (gdb) c Continuing. Hiya, Gump! Breakpoint 2, 0x00000000004007e8 in main () (gdb) (gdb) i r rax rsi rdx rdi rax 0x0 0 rsi 0x400843 4196419 rdx 0xc 12 rdi 0x1 1 Good... let's see what rsi register contains: (gdb) x/12cb 0x400843 0x400843 <.rodata+1>: 72 'H' 105 'i' 121 'y' 97 'a' 44 ',' 32 ' ' 71 'G' 117 'u' 0x40084b <.rodata+9>: 109 'm' 112 'p' 33 '!' 10 '\n' (gdb) So, my little knowledge junkies: - eax is where write syscall (4) is put - rdx is where the length (12) will be put - rdi keeps the file descriptor (STDOUT (1)) - rsi keeps the string ("Hiya, Gump!") ------------] Remember, remember... the template, the assembler! In case you have forgotten the asm template: .section.data [ you put initialized data here] .section .bss [ you put uninitialized data here] .section .text .globl _start _start: [you put yo code here] .section .rodata What do we have so far: .section .data .equ SYS_EXIT, 1 .equ SYS_WRITE, 4 .equ STDOUT, 1 .equ LENGTH, 12 ...and those registers (oh, my the pwn...): movl $SYS_WRITE, %eax #put sys_write in eax movq $STDOUT, %rdi #put fd in rdi movq $heyGump, %rsi #write string in rsi movq $LENGTH, %rdx #put length in rdx syscall pushq $SYS_EXIT; popq %rax #exit movq %rcx, %r10 syscall Time to see if there is something in .rodata: root@wrenchy:/home/testish # objdump -rs ./33 | grep -B0 -A1 .rodata Contents of section .rodata: 400842 00486979 612c2047 756d7021 0a00 .Hiya, Gump!.. root@wrenchy:/home/testish # Aand let's check the .bss: root@wrenchy:/home/testish # gdb -q --command=hehe.gdb --args ./33 Breakpoint 1, 0x00000000004007c9 in main () Dump of assembler code for function main: 0x00000000004007c5 <main+0>: push %rbp [....] (gdb) #check some info about the files (gdb) info files Symbols from "/usr/home/testish/33". Unix child process: Using the running image of child process 3905. While running this, GDB does not access memory from... Local exec file: `/usr/home/testish/33', file type elf64-x86-64-freebsd. Entry point: 0x400480 0x0000000000400200 - 0x0000000000400215 is .interp 0x0000000000400218 - 0x0000000000400248 is .note.tag 0x0000000000400248 - 0x000000000040027c is .hash [....] 0x0000000000600b08 - 0x0000000000600b18 is .data 0x0000000000600b18 - 0x0000000000600b30 is .bss 0x0000000800822190 - 0x00000008008272c0 is .hash in /lib/libc.so.7 [...] (gdb) disas 0x0000000000600b18,0x0000000000600b30 No function contains specified address. (gdb) q The program is running. Exit anyway? (y or n) y root@wrenchy:/home/testish # objdump -s -j .bss ./33 ./33: file format elf64-x86-64-freebsd root@wrenchy:/home/testish # Good, no bss. Feel free to use objdump for more details: root@wrenchy:/home/testish # objdump -d ./33 | grep -A10 _start 0000000000400480 <_start>: 400480: 55 push %rbp 400481: 48 89 e5 mov %rsp,%rbp 400484: 41 57 push %r15 [......] ... I guess...we're done! ------------] Ship it! Behold, almighty Gump on FreeBSD: root@wrenchy:/home/testish # more gumpbsd.s .section .data .equ SYS_EXIT, 1 .equ SYS_WRITE, 4 .equ KERNEL, 0x80 .equ STDOUT, 1 .equ LENGTH, 12 .section .rodata heyGump: .ascii "Hiya, Gump!\n" .section .text .globl _start _start: nop movl $SYS_WRITE, %eax movq $STDOUT, %rdi movq $heyGump, %rsi movq $LENGTH, %rdx syscall pushq $SYS_EXIT; popq %rax movq %rcx, %r10 syscall Assemble, link, and run: root@wrenchy:/home/testish # as gumpbsd.s -o gumpbsd.o root@wrenchy:/home/testish # ld gumpbsd.o -o gumpbsd root@wrenchy:/home/testish # ./gumpbsd Hiya, Gump! root@wrenchy:/home/testish # ... and check the syscalls: root@wrenchy:/home/testish # truss ./gumpbsd Hiya, Gump! write(1,"Hiya, Gump!\n",12) = 12 (0xc) exit(0x1) process exit, rval = 1 Close enough! ------------------] Why you're still reading this?! Let's see how it works: root@wrenchy:/home/testish # objdump -d ./gumpbsd ./gumpbsd: file format elf64-x86-64-freebsd Disassembly of section .text: 0000000000400078 <_start>: 400078: 90 nop 400079: b8 04 00 00 00 mov $0x4,%eax 40007e: 48 c7 c7 01 00 00 00 mov $0x1,%rdi 400085: 48 c7 c6 9d 00 40 00 mov $0x40009d,%rsi 40008c: 48 c7 c2 0c 00 00 00 mov $0xc,%rdx 400093: 0f 05 syscall 400095: 6a 01 pushq $0x1 400097: 58 pop %rax 400098: 49 89 ca mov %rcx,%r10 40009b: 0f 05 syscall Ahkay, a few nulls here and there: .section .data .equ SYS_EXIT, 1 .equ SYS_WRITE, 4 .equ KERNEL, 0x80 .equ STDOUT, 1 .equ LENGTH, 12 .section .rodata heyGump: .ascii "Hiya, Gump!\n" .section .text .globl _start _start: nop pushq $SYS_WRITE; popq %rax xorq %r9, %r9 movl %eax, %r9d pushq $STDOUT; popq %rdi pushq $heyGump; popq %rsi pushq $LENGTH; popq %rdx syscall pushq $SYS_EXIT; popq %rax #movq %rcx, %r10 pushq %rcx; popq %r10 syscall Let's see now: root@wrenchy:/home/testish # as gumpbsd.s -o gumpbsd.o root@wrenchy:/home/testish # ld gumpbsd.o -o gumpbsd root@wrenchy:/home/testish # ./gumpbsd Hiya, Gump! root@wrenchy:/home/testish # objdump -D ./gumpbsd ./gumpbsd: file format elf64-x86-64-freebsd Disassembly of section .text: 0000000000400078 <_start>: 400078: 90 nop 400079: 6a 04 pushq $0x4 40007b: 58 pop %rax 40007c: 4d 31 c9 xor %r9,%r9 40007f: 41 89 c1 mov %eax,%r9d 400082: 6a 01 pushq $0x1 400084: 5f pop %rdi 400085: 68 98 00 40 00 pushq $0x400098 40008a: 5e pop %rsi 40008b: 6a 0c pushq $0xc 40008d: 5a pop %rdx 40008e: 0f 05 syscall 400090: 6a 01 pushq $0x1 400092: 58 pop %rax 400093: 51 push %rcx 400094: 41 5a pop %r10 400096: 0f 05 syscall Disassembly of section .rodata: 0000000000400098 <heyGump>: 400098: 48 69 79 61 2c 20 47 imul $0x7547202c,0x61(%rcx),%rdi 40009f: 75 4000a0: 6d insl (%dx),%es:(%rdi) 4000a1: 70 21 jo 4000c4 <heyGump+0x2c> 4000a3: 0a .byte 0xa Almost there... need to fix gump's name! .section .data .equ SYS_EXIT, 1 .equ SYS_WRITE, 4 .equ KERNEL, 0x80 .equ STDOUT, 1 .equ LENGTH, 12 .section .rodata heyGump: .ascii "Hiya, Gump!\n" .section .text .globl _start _start: nop pushq $SYS_WRITE; popq %rax xorq %r9, %r9 movl %eax, %r9d pushq $STDOUT; popq %rdi # pushq $heyGump; popq %rsi movq $0xAAAAAa021706d75 , %rcx pushq %rcx movq $0x47202c617969480a, %rcx pushq %rcx xorq %rbx, %rbx xorq %rcx, %rcx movb $14, %cl movw %bx, (%rsp, %rcx, 1) movq %rsp, %rsi pushq $LENGTH; popq %rdx syscall pushq $SYS_EXIT; popq %rax #movq %rcx, %r10 pushq %rcx; popq %r10 syscall root@wrenchy:/home/testish #as gumpbsd.s -o gumpbsd.o root@wrenchy:/home/testish #ld gumpbsd.o -o gumpbsd root@wrenchy:/home/testish #./gumpbsd Hiya, Gump! Ah, so much better: root@wrenchy:/home/testish # objdump -d gumpbsd gumpbsd: file format elf64-x86-64-freebsd Disassembly of section .text: 0000000000400078 <_start>: 400078: 90 nop 400079: 6a 04 pushq $0x4 40007b: 58 pop %rax 40007c: 4d 31 c9 xor %r9,%r9 40007f: 41 89 c1 mov %eax,%r9d 400082: 6a 01 pushq $0x1 400084: 5f pop %rdi 400085: 48 b9 75 6d 70 21 a0 mov $0xaaaaaa021706d75,%rcx 40008c: aa aa 0a 40008f: 51 push %rcx 400090: 48 b9 0a 48 69 79 61 mov $0x47202c617969480a,%rcx 400097: 2c 20 47 40009a: 51 push %rcx 40009b: 48 31 db xor %rbx,%rbx 40009e: 48 31 c9 xor %rcx,%rcx 4000a1: b1 0e mov $0xe,%cl 4000a3: 66 89 1c 0c mov %bx,(%rsp,%rcx,1) 4000a7: 48 89 e6 mov %rsp,%rsi 4000aa: 6a 0c pushq $0xc 4000ac: 5a pop %rdx 4000ad: 0f 05 syscall 4000af: 6a 01 pushq $0x1 4000b1: 58 pop %rax 4000b2: 51 push %rcx 4000b3: 41 5a pop %r10 4000b5: 0f 05 syscall root@wrenchy:/home/testish # Extract opcodes: root@wrenchy:/home/testish # objdump -d gumpbsd | grep -v 'file' | cut -d: -f2 | cut -f1-7 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//g' | sed 's/ /\\x/g' | tr -d '\n' | sed 's/^/"/' | sed 's/$/"/g' "\x90\x6a\x04\x58\x4d\x31\xc9\x41\x89\xc1\x6a\x01\x5f\x48\xb9\x75\x6d\x70\x21\xa0\xaa \xaa\x0a\x51\x48\xb9\x0a\x48\x69\x79\x61\x2c\x20\x47\x51\x48\x31\xdb\x48\x31\xc9\xb1 \x0e\x66\x89\x1c\x0c\x48\x89\xe6\x6a\x0c\x5a\x0f\x05\x6a\x01\x58\x51\x41\x5a\x0f\x05" And there's your sh3llcode: more ship_it.c #include<sys/mman.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) int (*sc)(); char shellcode[] = "\x90\x6a\x04\x58\x4d\x31\xc9\x41\x89\xc1\x6a\x01\x5f\x48\xb9\x75\x6d\x70\x21\xa0\xaa \xaa\x0a\x51\x48\xb9\x0a\x48\x69\x79\x61\x2c\x20\x47\x51\x48\x31\xdb\x48\x31\xc9\xb1 \x0e\x66\x89\x1c\x0c\x48\x89\xe6\x6a\x0c\x5a\x0f\x05\x6a\x01\x58\x51\x41\x5a\x0f\x05" ; int main(int argc, char **argv) { void *ptr = mmap(0, sizeof(shellcode),PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0); if(ptr == MAP_FAILED) { handle_error("mmap"); } memcpy(ptr, shellcode, sizeof(shellcode)); sc = ptr; sc(); return 0; } Test it: root@wrenchy:/home/testish # gcc ship_it.c -o ship_it root@wrenchy:/home/testish # ./ship_it Hiya, Gump! ...and that's a wrap for today! Don't forget to check the links below! ^^ =========================== To be read: [Gump on Linux - 32 &64bit] https://jnc0x24dd099bb870.tumblr.com/Network/TCP/Sock5 [where all the kewl kidz are at] https://www.freebsd.org [PLT and GOT] https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html [GDB - Continuing and Stepping] https://sourceware.org/gdb/onlinedocs/gdb/Continuing-and-Stepping.html -------EOF---------