ret2sc for Root Privilege Escalation

저번 포스팅에선 쉘코드를 어떻게 만드는지에 대해 알아봤습니다. 이번엔 DEP 가 해제되어 있을 때 메모리에 쉘코드를 삽입하고 실행 흐름을 조작하는 기법인 ret2sc 에 대해 알아보겠습니다. 200n년도엔 공격자가 스택 영역에 쉘코드를 집어넣고 실행했던 일이 흔했지만 2004년부터 본격적으로 도입된 DEP 로 인해 많이 줄었습니다. 만약 DEP 가 걸려있다면, ret2libc 또는 ROP 기법을 보통 사용합니다. 이건 이후 포스팅에서 다루도록 하겠습니다. 아래 예제는 좀 극단적이지만 개발자가 실수로 보안 기능을 전부 끄고 사용자의 입력값을 검증 없이 그대로 복사한다고 가정해보겠습니다. ...

2019-07-09 · 3 min · 562 words · Byeongmin Bae

Writing Shellcode on Linux x86

이번 글에서는 pwner 의 기본기인 쉘코드 작성법에 대해 간단히 알아보겠습니다. 대회에 출제된 문제중에 이미 알려진 쉘코드를 사용해도 익스가 안될 때가 있어서 이럴땐 커스텀 쉘코드를 만드는 것이 좋습니다. 솔직히 요즘엔 다들 pwntools 의 shellcraft 를 써서 간단히 만들지만, 이번 포스팅에서는 How? 에 중점을 둬서 선조님들의 방식으로 한번 만들어보겠습니다. 쉘코드를 만들려면 먼저 c로 쉘을 실행하는 코드를 작성하고 타깃 아키텍처에 맞게 빌드해야 합니다. 그런 다음 바이너리를 분석해 어셈 코드로 재작성하고, 최종적으로 opcode를 추출하면 쉘코드가 완성됩니다. ...

2019-06-02 · 3 min · 627 words · Byeongmin Bae

LOB succubus (lv.18/20) Writeup

이번 문제는 간단한 ret2sc 문제입니다. /* The Lord of the BOF : The Fellowship of the BOF - nightmare - PLT */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dumpcode.h> main(int argc, char *argv[]) { char buffer[40]; char *addr; if(argc < 2){ printf("argv error\n"); exit(0); } // check address addr = (char *)&strcpy; if(memcmp(argv[1]+44, &addr, 4) != 0){ printf("You must fall in love with strcpy()\n"); exit(0); } // overflow! strcpy(buffer, argv[1]); printf("%s\n", buffer); // dangerous waterfall memset(buffer+40+8, 'A', 4); } 풀이에 앞서 먼저 조건을 살펴보겠습니다. ...

2019-05-26 · 2 min · 311 words · Byeongmin Bae

LOB zombie_assassin (lv.17/20) Writeup

이번 문제는 함수를 체이닝 하는 간단한 문제입니다. /* The Lord of the BOF : The Fellowship of the BOF - succubus - calling functions continuously */ #include <stdio.h> #include <stdlib.h> #include <dumpcode.h> // the inspector int check = 0; void MO(char * cmd) { if (check != 4) exit(0); printf("welcome to the MO!\n"); // olleh! system(cmd); } void YUT(void) { if (check != 3) exit(0); printf("welcome to the YUT!\n"); check = 4; } void GUL(void) { if (check != 2) exit(0); printf("welcome to the GUL!\n"); check = 3; } void GYE(void) { if (check != 1) exit(0); printf("welcome to the GYE!\n"); check = 2; } void DO(void) { printf("welcome to the DO!\n"); check = 1; } main(int argc, char * argv[]) { char buffer[40]; char * addr; if (argc < 2) { printf("argv error\n"); exit(0); } // you cannot use library if (strchr(argv[1], '\x40')) { printf("You cannot use library\n"); exit(0); } // check address addr = (char * ) & DO; if (memcmp(argv[1] + 44, & addr, 4) != 0) { printf("You must fall in love with DO\n"); exit(0); } // overflow! strcpy(buffer, argv[1]); printf("%s\n", buffer); // stack destroyer // 100 : extra space for copied argv[1] memset(buffer, 0, 44); memset(buffer + 48 + 100, 0, 0xbfffffff - (int)(buffer + 48 + 100)); // LD_* eraser // 40 : extra space for memset function memset(buffer - 3000, 0, 3000 - 40); } 여러 제약 조건들로 인해 출제자의 의도대로 DO(), GYE(), GUL(), YUT(), MO() 함수를 체이닝 할수밖에 없습니다. ...

2019-04-18 · 3 min · 428 words · Byeongmin Bae

Reverse Engineering Windows XP Minesweeper

이번 글에서는 Windows XP 지뢰찾기를 동적 분석하여 타이머를 수정하고 지뢰 배치를 확인할 수 있는 코드를 작성하는 과정을 살펴보겠습니다. 윈도우 GUI 프로그램은 기본적으로 창 단위로 구성되고, 해당 창에서 일어나는 이벤트를 핸들링 하기 위한 프로시저가 존재합니다. 지뢰찾기의 핵심 로직은 결국 이 프로시저에 존재하게 되어있습니다. 그렇다면 프로시저의 주소는 어떻게 찾을까요? 윈도우 API 를 사용해 창을 생성하려면 무조건 RegisterClass() 를 호출하여 해당 창의 정보를 등록해야 합니다. ATOM RegisterClassA( [in] const WNDCLASSA *lpWndClass ); 이때 넘어가는 WNDCLASSA 구조체를 살펴보면 프로시저의 주소값을 담는 lpfnWndProc 필드가 보입니다. ...

2019-04-10 · 3 min · 452 words · Byeongmin Bae

root-me.org ELF-x86-Format-string-bug-basic-2 Writeup

이번 문제는 FSB 를 트리거하여 check 변수의 값을 0xdeadbeef 로 덮는것이 목표입니다. printf() 계열 함수는 포맷 스트링을 인자로 받는데, 여기에 사용자 입력값이 들어가게 되면 입력값에 포함된 포맷스트링이 해석되어 FSB 취약점이 발생합니다. #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char **argv) { int var; int check = 0x04030201; char fmt[128]; if (argc < 2) exit(0); memset(fmt, 0, sizeof(fmt)); printf("check at 0x%x\n", & check); printf("argv[1] = [%s]\n", argv[1]); snprintf(fmt, sizeof(fmt), argv[1]); if ((check != 0x04030201) && (check != 0xdeadbeef)) printf("\nYou are on the right way !\n"); printf("fmt=[%s]\n", fmt); printf("check=0x%x\n", check); if (check == 0xdeadbeef) { printf("Yeah dude ! You win !\n"); setreuid(geteuid(), geteuid()); system("/bin/bash"); } } snprintf() 의 함수 원형을 보면 세번째 인자는 포맷 스트링이 들어가야 하는 자리이지만 문제 코드를 보면 세번째 인자에 사용자 입력값이 들어가고 있습니다. ...

2019-03-09 · 2 min · 249 words · Byeongmin Bae