Mommy! I think I know how to make shellcodes.
There is a readme file on the pwnable.kr server. In conclusion, "asm" demands a 64 bit Linux shellcode.
Before entering a shellcode, I looked "asm.c".
<"asm.c" fullcode>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <seccomp.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#define LENGTH 128
void sandbox(){
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
if (ctx == NULL) {
printf("seccomp error\n");
exit(0);
}
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
if (seccomp_load(ctx) < 0){
seccomp_release(ctx);
printf("seccomp error\n");
exit(0);
}
seccomp_release(ctx);
}
char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff";
unsigned char filter[256];
int main(int argc, char* argv[]){
setvbuf(stdout, 0, _IONBF, 0);
setvbuf(stdin, 0, _IOLBF, 0);
printf("Welcome to shellcoding practice challenge.\n");
printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\n");
printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\n");
printf("If this does not challenge you. you should play 'asg' challenge :)\n");
char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);
memset(sh, 0x90, 0x1000);
memcpy(sh, stub, strlen(stub));
int offset = sizeof(stub);
printf("give me your x64 shellcode: ");
read(0, sh+offset, 1000);
alarm(10);
chroot("/home/asm_pwn"); // you are in chroot jail. so you can't use symlink in /tmp
sandbox();
((void (*)(void))sh)();
return 0;
}
❑ Shell code : A small program that runs after a vulnerability attack.
❑ int setvbuf (FILE * stream, char * buf, int type, size_t size); : A function that changes the buffering way.
❍ int type : Setting type of buffering.
* _IONBF(Input Output with No BuFfering) : No buffering.
* _IOLBF(Input Output with Line BuFfering) : Passing data from buffer to device when newline characters(\n) appears.
* _IOFBF(Input Output with Full BuFfering) : Passing data from buffer to device when the set block size is exceeded.
❑ void * mmap (void * addr, size_t length, int prot, int flags, int fd, off_t offset) : Function to map physical address (as much as the length bytes from the offset) of device(fd) to virtual address(addr).
* On success, it returns a pointer to the mapped area. On failure, it returns -1.
❍ addr : The virtual address to map the physical space("0" assigns available virtual address).
❍ length : Size of space to map(Multiple of PAGE_SIZE)
❍ prot : Memory protection mode(e.g. 7 == Readable+Writeble+Executable)
* PROT_NONE(0), PROT_READ(1), PROT_WRITE(2), PROT_EXEC(4)
❍ flags : Property option.
* MAP_FIXED(Selecting only the specified address), MAP_SHARED(Sharing mappings with other processes)↔MAP_PRIVATE(No sharing mappings with other processes), MAP_ANONYMOUS(The fd argument is ignored and is initialized to zero)
❍ fd : File descriptor of the device.
❍ offset : Offset of physical address.
❑ void * memset ( void * addr, int value, size_t n ) : Function to fill memory from address addr as much as n bytes with the value argument.
* On success, it returns the addr pointer. On failure, it returns NULL.
❑ unsigned int alarm(unsigned int n) : Function that terminates program after n seconds.
❑ int chroot(const char * path) : Specifying a specific directory as the root directory("/").
❑ seccomp(Secure computing mode) : This is sandbox provided by the Linux kernel. It restricts the function of the opened file descriptor according to the specified rule.
❑ ((void (*)(void))sh)() : This is pointer-to-function type. This goes to the address "sh" and executes it with no argumnets and no return value.
* (((Expression)(Arguments))address)()
"sh" is allocated memory by mmap and filled with dummy values. Then, "stub" is entered. The shellcode that I made will be after "stub". Finally, "sh" is executed.
I disassembled the "stub". It cleans the registers by making it 0. No special other function.
This is my shellcode for reading the long named file. I used pwntools that is Python library. It just reads and writes.
Since the asm() output is broken, I encoded it with enhex().
I submitted the shellcode decoding the encoded code by "enhex()" like above.
Or, it is possible to submit the unbroken asm() shellcode like above.
There is one caution here. The value of ["print '] and ['print "] is defferent. ['print "] makes the values that is intend, because it provides a code to Python without being interpreted internally.
I disassembled the "stub". It cleans the registers by making it 0. No special other function.
* asm/disasm : Function of python's library pwntools.
This is my shellcode for reading the long named file. I used pwntools that is Python library. It just reads and writes.
❑ According to pwntools document, The file descriptor is stored in "rax" after using "open".
❑ According to pwntools document, The return value of "read()" is stored in "rsp".
❑ The value of "mySC" is assembly code and the value of "asm(mySC))" is hex code.
Since the asm() output is broken, I encoded it with enhex().
I submitted the shellcode decoding the encoded code by "enhex()" like above.
Or, it is possible to submit the unbroken asm() shellcode like above.
There is one caution here. The value of ["print '] and ['print "] is defferent. ['print "] makes the values that is intend, because it provides a code to Python without being interpreted internally.