- Backend Engineer · Stay Curious
12초 걸리던 쿼리 장애를 처음 겪고, 끝까지 파고든 기록
회사의 지식재산권 보호를 위해 도메인과 스키마, 쿼리를 가상의 도서관 도메인으로 완전히 재구성하였고 실제 회사에서 쓰이는 것과는 관련이 없음을 밝힙니다. 이 글에서는 오직 문제 해결 과정만을 다룹니다. 문제 정의 며칠 전 도서 검색 기능의 속도가 지나치게 느리다는 피드백을 받았습니다. 개발 환경에서는 성능 문제를 체감한 적이 없었고 사용 빈도도 높지 않았던 기능이라서, 솔직히 문제가 발생할 것이라고 예상하지 못했습니다 운영 환경에서 직접 재현해보니 검색어가 있을 때는 1초 미만이었지만, 없는 경우 약 12초의 지연이 발생했습니다. ...
Introduction to ptmalloc2 Heap Management and Exploitation
동적 할당 메커니즘에서는 성능을 위해 free 된 청크들을 재활용 합니다. 만약 이런 재활용 없이 시스템콜을 통해 매번 메모리를 할당받거나 반환한다면 유저-커널모드 전환 비용으로 인해 성능 하락이 심해집니다. 그래서 메모리 할당자는 기존 메모리 영역 재사용을 위한 힙 자료구조와 그에 맞는 최적의 힙 관리 알고리즘을 구현해놨습니다. 이번 포스팅에선 이러한 구조에 대해 이해하고 어떻게 exploit 할 수 있는지 알아보려 합니다. 이번에 살펴볼 메모리 할당자는 ptmalloc2 입니다. dlmalloc, TCMalloc 도 있지만, ptmalloc2 는 glibc 의 기본 할당자로 오랫동안 사용되어왔고, 리얼월드에서도 많이 쓰입니다. ...
File Stream Oriented Programming and Exploitation Techniques
이번 글에서는 최근에 공부했던 FSOP 를 포함한 여러 익스 테크닉들을 빠르게 다뤄보겠습니다. 메모 느낌이라 틀린 부분이 있을수도 있으니 가볍게 봐주시면 감사하겠습니다. _IO_FILE glibc의 파일 입출력은 _IO_FILE 구조체 하나로 구현되어 있습니다. stderr, stdout, stdin 같은 기본 스트림들도 전부 이 구조체 기반이고, scanf()가 어디에 쓸지, 버퍼를 어디서 읽을지 전부 이 구조체 안의 포인터들에 의해 결정됩니다. libc 내부에서는 스트림들이 _IO_FILE로 구현되어 싱글 링크드 리스트로 연결되어 있습니다. _IO_list_all -> _IO_2_1_stderr_.file._chain -> _IO_2_1_stdout_.file._chain -> _IO_2_1_stdin_.file._chain _IO_buf_base _IO_2_1_stdin_.file._IO_buf_base를 덮어쓸 수 있어야 함 이후 fgets(), scanf() 등 stdin을 사용하는 함수가 호출되어야 함 scanf()나 fgets() 같은 함수가 입력을 받을 때, _IO_buf_base 부터 _IO_buf_end까지의 영역을 버퍼로 사용합니다. 이 두 포인터를 원하는 주소로 조작하면, 다음에 scanf()가 호출될 때 해당 메모리 영역에 임의 쓰기가 가능합니다. ...
pwnable.kr horcruxes Writeup
이번 문제는 소스가 제공되지 않습니다. 일단 현재 상황을 먼저 알아보겠습니다. Voldemort concealed his splitted soul inside 7 horcruxes. Find all horcruxes, and ROP it! author: jiwon choi ssh horcruxes@pwnable.kr -p2222 (pw:guest) ==================================================== horcruxes@pwnable:~$ cat ./readme connect to port 9032 (nc 0 9032). the 'horcruxes' binary will be executed under horcruxes_pwn privilege. rop it to read the flag. horcruxes@pwnable:~$ file ./horcruxes ./horcruxes: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=bed2c3c01d21a3cbb1109e76a83310dfb8a077be, not stripped horcruxes@pwnable:~$ checksec --file=./horcruxes [*] '/home/horcruxes/horcruxes' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x809f000) asm 문제처럼 주어진 바이너리는 분석용이고 실제 익스는 nc 를 통해 해야 하네요 ...
pwnable.kr asm Writeup
이번 문제는 open, read, write, exit 시스템콜만으로 커스텀 쉘코드를 작성해서 flag 파일을 읽는것이 목표입니다. seccomp 우회라고 볼 수 있겠네요. /* Mommy! I think I know how to make shellcodes ssh asm@pwnable.kr -p2222 (pw: guest) =========================================================== asm@ubuntu:~$ checksec --file ./asm [*] '/home/asm/asm' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled Stripped: No */ #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; } 바이너리에 setgid 가 걸려있지 않아 readme 를 읽어봤더니 현재 asm, asm.c 는 분석용이고 권한상승 후 플래그를 얻으려면 nc 0 9026 에 페이로드를 전달해야 한다고 합니다. ...
pwnable.kr cmd2 Writeup
이번 문제는 특이하게 커멘드를 입력받아 system 함수로 실행해줍니다. 하지만 모든 환경 변수가 지워지고 PATH 도 이상하게 바뀝니다. 게다가 입력되는 커멘드에서 환경변수와 관련된 문자열도 필터링됩니다. /* Daddy bought me a system command shell. but he put some filters to prevent me from playing with it without his permission... but I wanna play anytime I want! ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1) =========================================================== cmd2@ubuntu:~$ checksec cmd2 [*] '/home/cmd2/cmd2' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No */ #include <stdio.h> #include <string.h> int filter(char* cmd){ int r=0; r += strstr(cmd, "=")!=0; r += strstr(cmd, "PATH")!=0; r += strstr(cmd, "export")!=0; r += strstr(cmd, "/")!=0; r += strstr(cmd, "`")!=0; r += strstr(cmd, "flag")!=0; return r; } extern char** environ; void delete_env(){ char** p; for(p=environ; *p; p++) memset(*p, 0, strlen(*p)); } int main(int argc, char* argv[], char** envp){ delete_env(); putenv("PATH=/no_command_execution_until_you_become_a_hacker"); if(filter(argv[1])) return 0; printf("%s\n", argv[1]); system( argv[1] ); return 0; } 처음엔 여러가지 명령어를 시도해봤는데 PATH 때문에 당연히 먹히지 않았습니다. ...
pwnable.kr passcode Writeup
이번 문제는 인증 절차를 무력화 시켜 플래그를 얻는 문제입니다. /* Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that? ssh passcode@pwnable.kr -p2222 (pw:guest) ========================================================= passcode@pwnable:~$ checksec ./passcode [*] '/home/passcode/passcode' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000) */ #include <stdio.h> #include <stdlib.h> void login() { int passcode1; int passcode2; printf("enter passcode1 : "); scanf("%d", passcode1); fflush(stdin); // ha! mommy told me that 32bit is vulnerable to bruteforcing :) printf("enter passcode2 : "); scanf("%d", passcode2); printf("checking...\n"); if (passcode1 == 338150 && passcode2 == 13371337) { printf("Login OK!\n"); system("/bin/cat flag"); } else { printf("Login Failed!\n"); exit(0); } } void welcome() { char name[100]; printf("enter you name : "); scanf("%100s", name); printf("Welcome %s!\n", name); } int main() { printf("Toddler's Secure Login System 1.0 beta.\n"); welcome(); login(); // something after login... printf("Now I can safely trust you that you have credential :)\n"); return 0; } 코드를 보면 scanf() 에 주소 연산자가 누락되어있어 두 passcode 변수는 입력을 받을 수 있는 상황이 아닙니다. ...
reversing.kr Easy Keygen Writeup
이번 문제는 간단한 키젠을 제작하는 문제입니다. 문제 설명을 보면 시리얼 키가 5B134977135E7D13 일때의 name 을 찾아달라고 합니다. 분석 도중 입력 받은 name 에 대해 조건에 따라 xor 연산을 반복하는 부분을 보았습니다. .text:00401000 var_130 = byte ptr -130h .text:00401000 var_12F = byte ptr -12Fh .text:00401000 var_12E = byte ptr -12Eh .text:00401038 mov [esp+140h+var_130], 10h .text:0040103D mov [esp+140h+var_12F], 20h .text:00401042 mov [esp+140h+var_12E], 30h var_130 은 0x10, 0x20, 0x30 세 값을 순환하며 name 배열의 각 바이트와 순차적으로 xor 연산을 수행합니다. ...
Bypassing ASLR/DEP via ROP for Root Privilege Escalation
저번 글에서는 ASLR이 비활성화된 환경에서 ret2libc 기법과 권한상승을 위한 ROP 개념을 다뤘었습니다. 하지만 현대적 시스템에서는 ASLR 이 기본적으로 활성화 되어있어 다음 실행 때 라이브러리의 주소를 정확히 “예측“하기는 어렵습니다. 물론 32비트에서 조건에 따라 브루트포스가 가능하긴 합니다. 그래서 이번 글에서는 동적 링킹 메커니즘을 이용해 libc leak 을 일으켜 ASLR/DEP 가 활성화 되어있는 환경에서도 exploit 을 성공시키는 방법에 대해 알아보겠습니다. 한가지 팁은 gdb 는 편리한 디버깅을 위해 ASLR 비활성화가 기본이라 실제 환경과 똑같이 만들려면 따로 커멘드를 쳐야 합니다. ...
Bypassing DEP via ret2libc and ROP for Root Privilege Escalation
저번 ret2sc 와 달리 ret2libc 는 매핑된 외부 라이브러리 함수를 이용해 쉘을 실행하기 때문에 DEP 가 걸려있어도 유효한 기법입니다. 만약 ASLR이 적용된 환경이라면 주소가 매번 변경되므로 주소 leak 과 오프셋 계산 과정이 추가적으로 들어가는데, 이 주제는 다음 글에서 다루도록 하겠습니다. 이번에는 ret2libc 기법으로 쉘을 획득하고 ROP 기법으로 권한상승까지 해보겠습니다. 저번과 똑같은 예제 코드지만 이번엔 스택 실행 권한이 없습니다. /* $ gcc -m32 -fno-stack-protector -mpreferred-stack-boundary=2 -no-pie $ cat /proc/sys/kernel/randomize_va_space # 0 Kernel: 4.4.0-142-generic x86_64 | gcc: 5.4.0 Checksec: Partial RELRO | No canary found | NX enabled | No PIE */ #include <stdio.h> #include <string.h> int main(int argc, char **argv) { char buf[32]; strcpy(buf, *(argv + 1)); printf("%s", buf); } system() 이 호출이 가능한지 확인하기 위해 ret 영역을 system() 의 주소로 덮어보겠습니다. ...