참고
※참고 자료는 드림핵을 이용하였습니다.
※드림핵의 로드맵대로 공부할 예정입니다.
※스스로를 위해 정리한 참고 자료입니다.

System Hacking101

System Hacking

  • 시스템 해킹(System Hacking)은 컴퓨터 프로그램의 행위를 조작하여 공격자가 원하는 행동을 실행하도록 하는 공격 전반을 일컫습니다.
  • '시스템 해킹'은 컴퓨터에서 구동되는 모든 프로그램에 대한 해킹에 적용 가능한 말입니다.


  • System Hacking 관련 강의에서 보게 되는 취약한 프로그램들은 대부분 C언어로 구현되었습니다.
  • C언어는 효율성과 유연함에 큰 강점이 있어 다양한 애플리케이션을 구현하는데 사용됩니다.
  • 여기서는 운영체제, 게임 등 굉장히 복잡한 프로그램도 포함됩니다.
    • 그렇기에, 시스템 프로그래밍 및 시스템 해킹을 공부하고자 한다면 C언어를 먼저 배우는걸 권장합니다.


  • 드림핵의 System Hacking 강의는 C 언어 기반 프로그램에서 발생할 수 있는 취약점에 대한 기본적인 지식과 이를 이해하기 위한 배경 지식을 설명하는 것을 목표로 합니다.
  • 여기에는 프로그램이 시스템 상에서 작동하는 방식, 프로그램을 분석하는데 필요한 도구, 취약점의 유형 및 이를 공략하는(익스플로잇하는) 방법론에 대한 설명이 포함되어 있습니다.


  • 시스템 해킹을 공부할 때는 시작하는 과정이 가장 어려운 단계 중 하나입니다. 요구하는 배경 지식의 범주가 상당히 넓은 편이기 때문입니다.


  • 예를 들어, 가장 기초적인 취약점인 스택 버퍼오버플로우(Stack Buffer Overflow)를 이해하려면 assembly 레벨에서의 메모리 사용과 같은 시스템 프로그래밍 관련 지식을 요구합니다.


  • 시스템 해킹의 목표는 통상적으로 공격 대상 프로그램이 구동되는 서버의 임의 유저와 같은 권한을 획득하는 것입니다. 원격 환경에 있는 공격자가 타겟 서버에서 운영체제의 명령어를 실행할 수 있는 공격을 원격코드실행(Remote Code Execution)이라고 부릅니다.

강의 수강에 필요한 사전 지식

시스템 해킹을 잘하기 위해서 C 언어가 컴파일 된 이후에 어떤 방식으로 동작하는지 이해하고 있어야 합니다. 따라서, 시스템 해킹을 공부하기에 앞서 C 언어를 배워야 합니다. 하지만 얼마나 자세하게 C 언어를 공부하고 이해해야 하는지에 대한 척도는 가늠하기 어렵습니다.


드림핵의 시스템 해킹 강의는 가능한 적은 사전 지식을 요구하는 형태로 구성되었습니다. 그럼에도, 원활한 강의 수강과 실습을 위해 C 언어에 대한 기본 지식은 필요하기 때문에, 리버싱 엔지니어링 강의를 수강한 후 시스템 해킹 강의를 수강하기를 권장합니다.

  • 아래는 간단한 C 코드입니다.
#include <stdio.h>

void fun (int x, int y)
{
	int z;	
	char buf[100];
	
	z = x;
	read (0, buf, y);

	printf ("x is %d\n", x);
	//printf (“z is %d\n”, z);
}

int main (int argc, char* argv[])
{
	int a, b;
	a = 30;
	b = 300;

	fun (a,b);
}

코드를 이해한 후 아래 체크리스트를 확인해 보세요.
1. 위 코드가 실행 되었을 때, 프로그램이 어떻게 동작할지 예상할 수 있다
2. calling convention이 무엇인지 설명할 수 있다.
3. stack frame이 무엇인지 설명할 수 있다.
4. register가 무엇인지 설명할 수 있으며, C 코드가 진행됨에 따라 레지스터 값들이 어떻게 변할지 유추할 수 있다.
5. 시작 시점부터 종료 시점까지 main 함수의 stack의 변화를 설명할 수 있다
6. 32bit 프로그램일 때와 64bit 프로그램일 때 stack 값들의 차이점을 설명할 수 있다.
7. fun 함수의 취약한 부분이 무엇인지 찾을 수 있다.
8. fun 함수의 취약점으로 인해 32bit와 64bit환경에서 x의 출력값이 어떻게 달라지는지 설명할 수 있다.
9. x가 아닌 z를 출력하는 경우, 32bit와 64bit환경에서 z의 출력값이 어떻게 달라지는지 설명할 수 있다.
10. Figure 1을 컴파일한 바이너리가 주어졌을 때, 이를 익스플로잇하여 셸(shell)을 획득하는 공격 코드를 작성할 수 있다.
11. 위 문항 중 체크할 수 있는 답변이 없다

  • 위 문항 중 11번만 체크할 수 있다면 리버싱 엔지니어링 강의를 우선적으로 수강해야 합니다.
  • 1번만 체크할 수 있다면, 시스템 해킹 강의를 수강하며 2~6번에 해당하는 키워드를 중심으로 공부를 진행하면 됩니다.
  • 해당 키워드를 모두 이해했다면, 강의를 통해 7~10번 내용을 학습하며 본격적으로 시스템 해킹을 이해할 수 있습니다.

Reverse Engineering

Reverse Engineering

리버스 엔지니어링(Reverse Engineering)은 줄여서 리버싱으로도 부릅니다.

  • 이름에서 알 수 있듯이 리버스 엔지니어링은 무언가를 설계하고 제작하는 '엔지니어링' 과정을 정반대로 수행하는 것입니다.
  • 컴퓨터 프로그래밍적 관점에서는 개발자가 소스 코드를 작성하고, 컴파일한 산출물에서부터 시작하는 것입니다.
  • 프로그램의 동작을 직접 실행 시켜보면서 소스 코드의 내용을 추측하는 것도 가능한 방법이고, 컴파일된 결과물의 데이터를 분석하여 소스 코드의 내용을 유추, 복구 해볼 수도 있습니다.


  • 위에서 언급한 산출물을 흔히 '프로그램' 또는 '이진 파일' 이라고 부릅니다.
  • 컴파일 되지 않은 코드 역시 '프로그램'이라고 부르는 경우가 있어 '이진 파일'이 조금 더 보편적으로 사용됩니다.
  • 이진 파일은 사람이 눈으로 식별하기 어려운 데이터로 이루어져 있어, 단순히 이 데이터 값을 본다고 해서 어떤 동작을 하는지 알아내기가 어렵습니다.


  • 이를 도와주는 것이 '디스어셈블러(disassembler)', '디컴파일러(decompiler)'와 같은 도구입니다.
  • 사람이 식별하기 어려운 데이터를, 보다 알아보기 쉬운 형태로 변형 시켜주는 역할을 합니다.


  • 사람이 알아볼 수 있는 프로그래밍 언어로 이루어진 프로그램을 분석하는 것도 리버싱이라고 부를 수 있을까요? 물론 그렇습니다.
  • 아무리 읽고 이해할 수 있는 텍스트로 이루어져 있다고 해도 단순히 살펴보는 것만으로는 프로그램의 전체 구조와 설계자의 의도를 이해할 수 없습니다.
  • 그렇기 때문에 ‘분석하는 데이터의 형태가 식별 가능한지’가 리버싱인지 아닌지를 결정하지는 않습니다.


  • 리버스 엔지니어링 강의는 시스템 해킹, 웹 해킹 카테고리에 비해 강의 수가 적습니다.
  • 그 이유는, 다른 강의에서 실습 문제를 분석하는 과정도 하나의 리버싱이기 때문입니다.
  • 간단하게 정리하면 리버싱은 '해킹'이면서 동시에 '분석'을 하는 분야입니다.

강의 수강에 필요한 사전 지식

첫째로, 리버스 엔지니어링을 통해 프로그램의 동작을 이해하기 위해서는 해당 프로그램을 구성하는 프로그래밍 언어를 이해할 수 있어야 합니다.

  • 예를 들어, C 언어로 소스 코드를 작성하고 컴파일한 프로그램은 아무리 사람에게 편한 형태로 복구되어도 C 언어가 최대치입니다.
  • 따라서 C 언어를 모른다면 이를 위한 디컴파일러의 동작은 효과를 보기 어렵습니다.



    둘째로, x86 assembly를 읽고 이해할 수 있으면 좋습니다. 물론 x86 assembly에 대한 강의도 존재하기에 처음부터 숙지해야 할 필요는 없습니다.
  • 어셈블리어가 한 종류만 있는 것은 아니지만 기본적으로 복잡하게 구성된 x86 assembly를 이해할 수 있다면 다른 아키텍쳐의 어셈블리어는 보다 손쉽게 습득하실 수 있습니다.
  • 어셈블리어를 이해한다는 말은, 프로그램이 low-level에서 메모리를 어떤 방식으로 사용하는지 이해하고 있다는 말과 같습니다.
  • 디컴파일러가 없는 환경에서 리버싱을 해야할 수도 있기 때문에 어셈블리어를 읽고 이해하는 능력은 앞으로 필수적입니다.
예시 C코드
#include <stdio.h>

int enc[17] = {75, 104, 111, 111, 114, 35, 71, 117, 104, 100, 112, 107, 100, 102, 110, 36, 3};

int main(){

    int key = 0;
    int dec[17];

    for(int i=0; i<17; i++){
        printf("%c", enc[i]);
    }

    printf("\nYour input: ");
    scanf("%d", &key);

    for(int i=0; i<17; i++){
        dec[i] = enc[i] - key;
        printf("%c", dec[i]);
    }
    printf("\n");

    return 0;
}
  • 스스로 얼마나 준비가 되었는지 알아보기 위해 1) 위와 같이 C 언어로 작성된 간단한 소스코드와 2) 아래와 같이 해당 소스코드의 실행파일을 disassemble한 어셈블리 코드를 준비했습니다. 두 코드를 보고 아래의 질문을 얼마나 답할 수 있는지 체크해보세요!
예시 assembly 코드
Dump of assembler code for function main:
   0x00005555555551a9 <+0>:     endbr64 
   0x00005555555551ad <+4>:     push   rbp
   0x00005555555551ae <+5>:     mov    rbp,rsp
   0x00005555555551b1 <+8>:     sub    rsp,0x60
   0x00005555555551b5 <+12>:    mov    rax,QWORD PTR fs:0x28
   0x00005555555551be <+21>:    mov    QWORD PTR [rbp-0x8],rax
   0x00005555555551c2 <+25>:    xor    eax,eax
   0x00005555555551c4 <+27>:    mov    DWORD PTR [rbp-0x5c],0x0
   0x00005555555551cb <+34>:    mov    DWORD PTR [rbp-0x58],0x0
   0x00005555555551d2 <+41>:    jmp    0x5555555551f6 <main+77>
   0x00005555555551d4 <+43>:    mov    eax,DWORD PTR [rbp-0x58]
   0x00005555555551d7 <+46>:    cdqe   
   0x00005555555551d9 <+48>:    lea    rdx,[rax*4+0x0]
   0x00005555555551e1 <+56>:    lea    rax,[rip+0x2e38]        # 0x555555558020 <enc>
   0x00005555555551e8 <+63>:    mov    eax,DWORD PTR [rdx+rax*1]
   0x00005555555551eb <+66>:    mov    edi,eax
   0x00005555555551ed <+68>:    call   0x555555555080 <putchar@plt>
   0x00005555555551f2 <+73>:    add    DWORD PTR [rbp-0x58],0x1
   0x00005555555551f6 <+77>:    cmp    DWORD PTR [rbp-0x58],0x10
   0x00005555555551fa <+81>:    jle    0x5555555551d4 <main+43>
   0x00005555555551fc <+83>:    lea    rax,[rip+0xe01]        # 0x555555556004
   0x0000555555555203 <+90>:    mov    rdi,rax
   0x0000555555555206 <+93>:    mov    eax,0x0
   0x000055555555520b <+98>:    call   0x5555555550a0 <printf@plt>
   0x0000555555555210 <+103>:   lea    rax,[rbp-0x5c]
   0x0000555555555214 <+107>:   mov    rsi,rax
   0x0000555555555217 <+110>:   lea    rax,[rip+0xdf4]        # 0x555555556012
   0x000055555555521e <+117>:   mov    rdi,rax
   0x0000555555555221 <+120>:   mov    eax,0x0
   0x0000555555555226 <+125>:   call   0x5555555550b0 <__isoc99_scanf@plt>
   0x000055555555522b <+130>:   mov    DWORD PTR [rbp-0x54],0x0
   0x0000555555555232 <+137>:   jmp    0x55555555526f <main+198>
   0x0000555555555234 <+139>:   mov    eax,DWORD PTR [rbp-0x54]
   0x0000555555555237 <+142>:   cdqe   
   0x0000555555555239 <+144>:   lea    rdx,[rax*4+0x0]
   0x0000555555555241 <+152>:   lea    rax,[rip+0x2dd8]        # 0x555555558020 <enc>
   0x0000555555555248 <+159>:   mov    eax,DWORD PTR [rdx+rax*1]
   0x000055555555524b <+162>:   mov    ecx,DWORD PTR [rbp-0x5c]
   0x000055555555524e <+165>:   sub    eax,ecx
   0x0000555555555250 <+167>:   mov    edx,eax
   0x0000555555555252 <+169>:   mov    eax,DWORD PTR [rbp-0x54]
   0x0000555555555255 <+172>:   cdqe   
   0x0000555555555257 <+174>:   mov    DWORD PTR [rbp+rax*4-0x50],edx
   0x000055555555525b <+178>:   mov    eax,DWORD PTR [rbp-0x54]
   0x000055555555525e <+181>:   cdqe   
   0x0000555555555260 <+183>:   mov    eax,DWORD PTR [rbp+rax*4-0x50]
   0x0000555555555264 <+187>:   mov    edi,eax
   0x0000555555555266 <+189>:   call   0x555555555080 <putchar@plt>
   0x000055555555526b <+194>:   add    DWORD PTR [rbp-0x54],0x1
   0x000055555555526f <+198>:   cmp    DWORD PTR [rbp-0x54],0x10
   0x0000555555555273 <+202>:   jle    0x555555555234 <main+139>
   0x0000555555555275 <+204>:   mov    edi,0xa
   0x000055555555527a <+209>:   call   0x555555555080 <putchar@plt>
   0x000055555555527f <+214>:   mov    eax,0x0
   0x0000555555555284 <+219>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x0000555555555288 <+223>:   sub    rdx,QWORD PTR fs:0x28
   0x0000555555555291 <+232>:   je     0x555555555298 <main+239>
   0x0000555555555293 <+234>:   call   0x555555555090 <__stack_chk_fail@plt>
   0x0000555555555298 <+239>:   leave  
   0x0000555555555299 <+240>:   ret    
End of assembler dump.
  1. 프로그램이 실행 되었을 때 프로그램이 어떠한 동작을 하는지 설명할 수 있다.
  2. c 코드의 enc[17] 와 key 가 메모리에 배치될 때 각각 어떠한 메모리 세그먼트에 위치할지 대략적으로 설명할 수 있다.
  3. 메모리와 레지스터의 차이를 설명할 수 있다.
  4. 명령어 집합 구조(Instruction Set Architecture, ISA)가 무엇인지 설명할 수 있다.
  5. c 코드가 어떠한 과정을 거쳐 컴파일 되는지, 그리고 컴파일 과정에서 코드가 어떻게 바뀌는지 설명할 수 있다.
  6. c, 어셈블리, 바이너리 코드 각각의 특징과 그 차이점에 대해 설명할 수 있다.
  7. 어셈블리 코드를 보고 main 함수의 stack frame 구조를 파악할 수 있다.
  8. 어셈블리 코드를 보고 메모리와 레지스터를 구분할 수 있다.
  9. 어셈블리를 보고 동일한 기능을 하는 C 코드를 작성할 수 있다
  10. c 코드에서 int형 배열이 문자로 출력될 수 있는 이유를 설명할 수 있다.
  11. “Hello dreamhack!” 을 출력하도록 하는 key 값을 구할 수 있다.
  • 만약 1번 질문부터 답을 할 수 없거나 질문이 이해가 안 된다면 강의를 수강하거나 실습 문제를 해결하는 데 어려움이 있을 수 있습니다.
  • 먼저 C 언어를 공부한 후 리버싱에 대해 공부해야 합니다.
  • 최소 4번 질문까지 답변을 할 수 있는 상태에서 로드맵을 수강 하시길 추천합니다.
  • 5~11번 질문 중 답을 할 수 없는 질문이거나 처음 들어보는 키워드가 있다면 강의를 수강하며 배워갈 수 있습니다.
  • 혹은 스스로 질문에 대한 답을 찾아본 후 로드맵을 수강하며 답을 확인해보세요.

Web Hacking 101

Web Hacking

본 강의는 웹 해킹이 무엇인지 소개하고, Web Hacking 로드맵과 강의 수강에 필요한 지식을 설명합니다.

웹 해킹(Web Hacking)은 웹 상에서 본래의 의도와 다른 동작을 일으키거나 데이터를 도용, 변조, 시스템을 손상시키는 등의 악의적인 행위를 수행하는 것을 말합니다.

  • 웹으로 제공하는 서비스가 매우 다양해지고 복잡한 기능을 구현하면서, 의도치 않은 동작을 일으킬 수 있는 웹 해킹의 위협도 증가하였습니다.
  • 따라서 웹에서 발생할 수 있는 취약점을 알고 방어 대책을 연구하는 것의 중요성이 커지고 있습니다.

드림핵에는 다음과 같이 총 세 가지 Web Hacking 로드맵이 기본적으로 존재합니다.

  • Web Hacking Fundamental
  • Web Hacking Advanced - Server Side
  • Web Hacking Advanced - Client Side

Web Hacking Fundamental을 우선 수강 후, Web Hacking Advanced를 들으시는 것을 추천해 드립니다.

Web Hacking Fundamental

Web Hacking Fundamental 카테고리의 강의는 안전한 웹 서비스의 구현을 위해 반드시 알아둬야 할 웹 해킹의 기초적인 지식과 기술을 전달하는 것을 목표로 합니다.
해킹에 대한 막연한 호기심으로 드림핵을 방문하신 분들이 최대한 쉽게 웹 해킹에 입문할 수 있도록 도울 것입니다.

Web Hacking Fundamental 카테고리의 강의는 웹의 기반이 되는 프로토콜인 HTTP/HTTPS와, 웹 브라우저의 기본적인 원리부터 배웁니다. 이를 시작으로 클라이언트 사이드에서 일어날 수 있는 공격 기법과, 서버 사이드에서 일어날 수 있는 공격 기법을 배우고 실습해보며 웹 해킹의 기초적인 지식과 기술을 익힐 수 있습니다.

Web Hacking Advanced - Server Side

Web Hacking Advanced - Server Side 로드맵은 Web Hacking Fundamental 로드맵의 배경 지식을 기반으로, 보다 심화된 내용을 다룹니다.
특히 서버 사이드에서 일어날 수 있는 공격 기법과 보안에 초점을 맞춥니다.

  • Web Hacking Advanced - Server Side의 카테고리는 웹 서버 개발 시 사용되는 다양한 데이터베이스들을 알아보고 각 데이터베이스를 대상으로 수행할 수 있는 SQL Injection 공격 기법을 배우고 실습합니다.
  • 그리고 서버의 운영체제에서 악의적인 명령어를 실행하는 공격인 Command Injection을 배웁니다. 또한 웹 서버에 파일을 업로드하거나 웹 서버로부터 파일을 다운로드할 수 있을 때 수행할 수 있는 공격 기법도 익혀봅니다.

Web Hacking Advanced - Client Side

Web Hacking Advanced - Client Side 로드맵은 Web Hacking Fundamental 강의의 배경 지식을 기반으로, 보다 심화된 내용을 다룹니다. 특히 클라이언트 사이드에서 일어날 수 있는 공격 기법과 보안에 초점을 맞춥니다.

  • Web Hacking Advanced - Client Side의 카테고리는 공격자가 웹 리소스에 악성 스크립트를 삽입하여 이용자의 웹 브라우저에서 이를 실행하는 공격 기법인 XSS를 배웁니다.
  • 그리고 이를 막는 여러 가지 필터링을 배우고 이를 우회하는 방법도 시도해봅니다.
  • 브라우저는 악성 스크립트의 실행을 막고 브라우저가 가지고 있는 중요한 데이터가 외부로 유출되지 않도록 다양한 보호 기법이 적용되어 있습니다.
  • 현대 브라우저에 적용된 보호 기법들과 이를 우회하는 방법들도 알아봅니다.
  • 그 밖에 Template Injection, CSS Injection, Relative Path Overwrite, 그리고 DOM XSS 등 클라이언트 사이드를 대상으로한 다양한 공격 기법들도 배우고 실습해봅니다.

강의 수강에 필요한 사전 지식

웹 해킹을 잘 하려면 웹 개발 지식이 필요하다는 말이 있습니다. 이는 맞는 말입니다.

  • 웹 개발자가 작성한 코드를 이해하고 분석하여 취약점을 찾아내기 위해서는 웹 개발에 대한 기본적인 이해와 지식이 필요합니다.
  • 따라서 웹 해킹을 잘하려는 사람들에게 웹 개발은 필수적인 덕목 중 하나입니다.
    -웹 해킹을 위해서는 웹 프로토콜과 웹 서버, 웹 어플리케이션의 작동 방식을 이해하는 것이 중요합니다.
  • 웹 개발에서 사용되는 언어와 프레임워크에 대한 이해도도 높여야 하며, 데이터베이스와 여러 보안 기술에 대한 이해 역시 필요합니다.

하지만, 웹 개발자와 웹 해커는 서로 다른 분야이기 때문에, 웹 해킹을 하려는 사람들이 반드시 웹 개발을 전문적인 수준으로 공부할 필요는 없습니다.

  • 웹 개발자의 입장에서 생각하여 웹 서비스를 분석하고, 취약점을 찾는 것이 중요하지만, 웹 해킹에서 사용되는 다양한 도구와 기술들에 대한 이해와 활용 능력도 길러야하기 때문입니다.

드림핵의 웹 해킹 강의는 적어도 Flask, NodeJS, 그리고 Spring 중 한 가지 프레임워크로 작성된 웹 소스를 읽고 이해할 수 있는 수준임을 전제로 합니다.

  • 만약 관련 지식이 없다면, 책 또는 온라인에 공개된 웹 개발 강의를 통해 기초적인 웹 서비스를 만들어보시고 난 후 수강하시기를 추천해 드립니다.

수월한 강의 수강을 위해 어느 정도로 웹 개발을 잘해야 하나요? 에 대한 답을 찾으실 수 있도록 아래의 웹 소스 코드와 몇 가지 질문을 준비해보았습니다.

  • 아래의 소스 코드는 Flask와 MySQL을 활용하여 구현된 게시판 웹 서비스입니다. 전체 소스 코드는 본 링크를 통해 다운로드할 수 있습니다.
Flask와 MySQL을 활용하여 구현된 게시판 웹 서비스
#!/usr/bin/env python3
import os
import pymysql
from flask import Flask, abort, redirect, render_template, request

PAGINATION_SIZE = 10

app = Flask(__name__)
app.secret_key = os.urandom(32)

def connect_mysql():
    conn = pymysql.connect(host='db',
                           port=3306,
                           user=os.environ['MYSQL_USER'],
                           passwd=os.environ['MYSQL_PASSWORD'],
                           db='board',
                           charset='utf8mb4')
    cursor = conn.cursor()
    return conn, cursor

@app.route('/')
def index():
    return redirect('/board')

@app.route('/board')
def board():

    page = request.args.get('page')
    page = int(page) if page and page.isdigit() and int(page) > 0 else 1

    ret = []

    conn, cursor = connect_mysql()
    try:
        query = 'SELECT _id, title FROM posts ORDER BY _id DESC LIMIT %s, %s'
        cursor.execute(query, ((page - 1) * PAGINATION_SIZE, PAGINATION_SIZE))
        ret = cursor.fetchall()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    return render_template('board.html', page=page, ret=ret)

@app.route('/board/<post_id>')
def board_post(post_id):

    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    ret = None

    conn, cursor = connect_mysql()
    try:
        query = 'SELECT title, content FROM posts WHERE _id = %s'
        cursor.execute(query, (post_id))
        ret = cursor.fetchone()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    if not ret:
        abort(404)

    return render_template('post.html', title=ret[0],
                           content=ret[1], post_id=post_id)

@app.route('/write_post', methods=['POST'])
def write_post():
    if 'title' not in request.form or 'content' not in request.form:
        return render_template('write_post.html')

    title = request.form['title']
    content = request.form['content']

    conn, cursor = connect_mysql()
    try:
        query = 'INSERT INTO posts (title, content) VALUES (%s, %s)'
        cursor.execute(query, (title, content))
        conn.commit()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    return redirect('/board')

@app.route('/modify_post', methods=['POST'])
def modify_post():
    post_id = request.form['post_id']

    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    if 'title' not in request.form or 'content' not in request.form:
        conn, cursor = connect_mysql()
        try:
            query = 'SELECT title, content FROM posts WHERE _id = %s'
            cursor.execute(query, (post_id, ))
            ret = cursor.fetchone()
        except Exception as e:
            print(e, flush=True)
            abort(400)
        finally:
            cursor.close()
            conn.close()

        if not ret:
            abort(404)

        return render_template('modify_post.html', title=ret[0],
                               content=ret[1], post_id=post_id)

    title = request.form['title']
    content = request.form['content']

    conn, cursor = connect_mysql()
    try:
        query = 'UPDATE posts SET title=%s, content=%s WHERE _id = %s'
        cursor.execute(query, (title, content, post_id, ))
        conn.commit()
    except Exception as e:
        print(e, flush=True)
    finally:
        cursor.close()
        conn.close()

    return redirect(f'/board/{post_id}')

@app.route('/delete_post', methods=['POST'])
def delete_post():

    post_id = request.form['post_id']
    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    if 'answer' not in request.form:
        return render_template('delete_post.html', post_id=post_id)

    if request.form['answer'] == 'y':
        conn, cursor = connect_mysql()
        try:
            query = 'DELETE FROM posts WHERE _id = %s'
            cursor.execute(query, (post_id, ))
            conn.commit()
        except Exception as e:
            print(e, flush=True)
        finally:
            cursor.close()
            conn.close()

        return redirect('/board')

    return redirect(f'/board/{post_id}')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)
  1. 웹 브라우저와 웹 서버가 통신하는 프로토콜의 약자를 설명할 수 있다.
  2. 웹 사이트 주소에 포함된 "https" 중 "s"가 어떤 의미를 나타내는지 설명할 수 있다.
  3. 웹 브라우저와 서버가 이용자의 로그인 상태를 어떤 방법으로 유지하는지 설명할 수 있다.
  4. 요청을 성공적으로 처리하였을 때, 웹 서버가 어떤 HTTP 상태 코드를 반환해야 하는지 설명할 수 있다.
  5. 클라이언트의 요청에 오류가 있어서 요청을 처리하지 못했을 때, 웹 서버가 어떤 HTTP 상태 코드를 반환해야 하는지 설명할 수 있다.
  6. 웹 서버 측에서 발생한 오류로 인해 요청을 처리하지 못했을 때, 웹 서버가 어떤 HTTP 상태 코드를 반환해야 하는지 설명할 수 있다.
  7. 클라이언트와 서버 간의 데이터 전송 시, 인코딩과 암호화 간의 주요한 차이점을 설명할 수 있다.
  8. 웹 애플리케이션에서 사용자의 입력을 검증하는 이유와 그 중요성을 설명할 수 있다.
  9. HTTP 메소드에는 어떠한 종류가 있는지 나열할 수 있다.
  10. GET 메소드와 POST 메소드의 차이가 무엇인지 설명할 수 있다.
  11. 위의 웹 서비스에 어떤 엔드포인트들이 존재하는지 설명할 수 있다.
  12. 각 엔드포인트가 수행하는 주요 기능에 대해 설명할 수 있다.
  13. 각 기능을 수행하기 위해 사용되는 SQL 쿼리를 이해하고 설명할 수 있다.
  14. 이용자가 POST /board/<post_id> 엔드포인트를 통해 게시글을 생성하는 경우, 브라우저, 웹 서비스, 그리고 데이터베이스 간의 상호작용 과정을 상세히 설명할 수 있다.
  • 1번부터 10번까지의 질문은 웹 기초 지식에 관련된 내용이고, 11번부터 14번까지는 위 소스 코드와 관련된 질문입니다.
  • 만약 질문에 답을 할 수 없거나 질문이 이해가 안 된다면 강의를 수강하거나 실습 문제를 해결하는 데 어려움이 있을 수 있으므로, 구글링을 통해 Flask 프레임워크와 MySQL 데이터베이스를 사용한 간단한 웹 개발 공부를 먼저 선행해 보시기를 권장합니다.

게시판 구동 방법

소스 코드를 눈으로만 보는 것 뿐만 아니라 직접 웹 서비스를 실행하여 결과값을 확인하면서 분석을 하면 더 빠르고 쉽게 이해하며 공부할 수 있습니다.

  • 리눅스에서 도커를 사용하여 본 게시판 웹 서비스를 구동하는 방법을 설명하겠습니다.
  • Docker 사용법이 아직 익숙치 않은 경우, 이후의 Docker 강의를 수강하면서 사용 방법을 먼저 숙지한 후 실습해 보시기를 바랍니다.

링크를 통해 게시판을 다운로드한 후 압축을 풀어보면 다음과 같은 구조를 지닙니다.

$ tree .
.
`-- my_simple_board
    |-- deploy
    |   |-- app
    |   |   |-- app
    |   |   |   |-- app.py
    |   |   |   |-- run.sh
    |   |   |   `-- templates
    |   |   |       |-- board.html
    |   |   |       |-- delete_post.html
    |   |   |       |-- modify_post.html
    |   |   |       |-- post.html
    |   |   |       `-- write_post.html
    |   |   |-- Dockerfile
    |   |   `-- requirements.txt
    |   `-- db
    |       `-- 1_init.sql
    `-- docker-compose.yml

6 directories, 11 files
$
  • 우선 my_simple_board 디렉토리로 이동합니다. ls 명령어를 치면 다음과 같은 결과가 나와야 합니다.
$ ls
deploy  docker-compose.yml
$
  • 이후 다음과 같이 docker compose up 명령어를 실행하고 기다리면 게시판 웹 서비스가 실행됩니다.
$ docker compose up
[+] Building 4.2s (14/14) FINISHED

...

my_simple_board-db-1   | 2022-11-29  7:57:48 0 [Note] Server socket created on IP: '::'.
my_simple_board-db-1   | 2022-11-29  7:57:48 0 [Note] InnoDB: Buffer pool(s) load completed at 221129  7:57:48
my_simple_board-db-1   | 2022-11-29  7:57:48 0 [Note] mariadbd: ready for connections.
my_simple_board-db-1   | Version: '10.9.3-MariaDB-1:10.9.3+maria~ubu2204'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution
my_simple_board-app-1  |  * Serving Flask app 'app'
my_simple_board-app-1  |  * Debug mode: off
my_simple_board-app-1  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
my_simple_board-app-1  |  * Running on all addresses (0.0.0.0)
my_simple_board-app-1  |  * Running on http://127.0.0.1:8000
my_simple_board-app-1  |  * Running on http://172.16.29.3:8000
my_simple_board-app-1  | Press CTRL+C to quit
  • 이후 웹 브라우저를 통해 해당 웹서버의 8000포트로 접속하면 아래와 같이 게시판에 접속할 수 있습니다.

Cryptography 101

Cryptography

암호학(Cryptography)은 정보를 보호하거나 안전하게 통신하기 위한 방법론을 다루는 학문입니다.

  • 더 자세히 말하면, 키(Key)를 이용하여 평문(Plaintext)을 암호문(Ciphertext)으로 변환하거나, 암호문을 평문으로 변환하는 전반적인 과정을 일컫습니다.
  • 암호학은 개인정보 관리, 전자상거래, 클라우드 서비스 등 보안을 유지해야 하는 다양한 영역에서 사용됩니다.

암호학은 깊게 파고들수록 수학 지식이 많이 필요하지만, 드림핵의 Cryptography 강의는 암호학을 공부해본 적 없는 분들에게 암호학을 넓고 얕게 전달하는 것을 목표로 하고 있습니다.

따라서 많은 사전 지식을 요구하지는 않으나 다음에 나오는 지식을 알고 있으면 강의를 수강하고 워게임을 해결해 나가는데 있어서 많은 도움이 됩니다.

강의 수강에 필요한 사전 지식

비트 연산(Bit Operation)

다양한 비트 연산자가 어떤 연산을 하는지 미리 알아야 강의를 수월하게 수강할 수 있습니다.

  • 비트 연산에는 AND, OR, XOR, NOT, SHIFT 등이 있습니다. 비트 연산은 많은 암호학적인 알고리즘의 기반이 되므로 필수로 알아야 합니다.
  • 강의를 수강하거나 워게임 해결을 시도할 때 또한 많은 비트 연산을 마주치게 됩니다.
  • 새로운 비트 연산을 만날 때마다 공부해도 되지만 미리 각 비트 연산이 어떤 연산을 하는지 미리 이해하고 시작하신다면 좀더 수월하게 암호학 강의를 수강할 수 있습니다.

모듈로 연산(Modulo Operation)

모듈로 연산이 기본적으로 어떤 연산을 하는지 알면 강의를 수월하게 수강할 수 있습니다.

  • 모듈로 연산(Modulo Operation)은 나머지 연산이라고도 불리며, 어떤 숫자를 다른 숫자로 나눈 나머지 값을 구하는 연산입니다.
  • 많은 암호화 알고리즘에서 공개키와 개인키 및 소수를 나타내기 위해 큰 정수가 사용되며, 모듈로 연산은 큰 정수에 대한 산술을 효율적으로 연산하는 방법을 제공하므로 암호학을 공부하려면 반드시 알아야 하는 연산입니다.

  • 모듈로 연산은 다양한 법칙과 특징을 가지고 있습니다.
  • 지만 드림핵의 암호학 강의는 모든 법칙과 특징을 설명하지 않습니다.
  • 하나의 개념을 설명할 때 필요할 때 필요한 지식을 개괄적으로 설명하고 넘어가므로 미리 깊게 모듈로 연산을 미리 깊게 공부할 필요는 없습니다.
  • 기본적으로 어떤 연산을 하는지만 알고가면 됩니다.

Python

Python은 미리 필수적으로 알고 강의를 수강하여야 수월하게 내용을 이해할 수 있습니다.

  • 강의에서 암호화 알고리즘의 구현을 보여줄 때 Python으로 작성된 코드를 기준으로 설명합니다.
  • 또한 드림핵의 암호학 워게임이 제공하는 파일은 Python으로 작성된 파일들이 많습니다.
  • 따라서 기본적으로 Python을 읽고 사용할 수 있는 수준이 되어야 수월하게 강의를 수강하고 워게임을 해결해나갈 수 있습니다.

스스로 얼마나 준비가 되었는지 알아보기 위해 아래의 Python 언어로 작성된 간단한 퀴즈들과, 이 퀴즈들을 풀기 위해 필요한 개념을 체크하는 질문을 준비했습니다.

import random


def bit_quiz1():
    print("bit operation quiz1")
    a = random.randint(0, 255)
    b = random.randint(0, 255)
    print(f"{a = }")
    print(f"{b = }")

    if int(input("a ^ b = ")) != a ^ b:
        exit("Wrong!")
    if int(input("a & b = ")) != a & b:
        exit("Wrong!")
    if int(input("a | b = ")) != a | b:
        exit("Wrong!")
        
    print("Good Job!")


def bit_quiz2():
    print("bit operation quiz2")
    a = random.randint(0, 255)
    b = random.randint(0, 255)
    target = a * 256 + b
    i = int(input())
    if (a << i) ^ b != target:
        exit("Wrong!")
        
    print("Good Job!")


def modulo_quiz1():
    print("modulo quiz1")
    p = 0x10001
    a = random.randint(1, p)
    b = random.randint(1, p)
    print(f"{a = }")
    print(f"{b = }")

    if int(input("a + b = ? (mod p) > ")) != (a + b) % p:
        exit("Wrong!")
    if int(input("a - b = ? (mod p) > ")) != (a - b) % p:
        exit("Wrong!")
    if int(input("a * b = ? (mod p) > ")) != (a * b) % p:
        exit("Wrong!")
        
    print("Good Job!")


def modulo_quiz2():
    print("modulo quiz2")
    p = 15260339158265275051  # 64bit prime
    a = random.randint(1, p)
    b = random.randint(1, p)
    print(f"{a = }")
    print(f"{b = }")

    d = int(input("a / b = ? (mod p) > "))
    if (d * b - a) % p != 0:
        exit("Wrong!")
        
    print("Good Job!")


def modulo_quiz3():
    print("modulo quiz3")
    p = 15260339158265275051  # 64bit prime
    a = random.randint(1, p)
    b = random.randint(1, p)
    print(f"{a = }")
    print(f"{b = }")

    if int(input("a**b = ? (mod p) > ")) != pow(a, b, p):
        exit("Wrong!")
        
    print("Good Job!")


if __name__ == "__main__":
    bit_quiz1()
    bit_quiz2()
    modulo_quiz1()
    modulo_quiz2()
    modulo_quiz3()

    flag = open("flag", "rb").read()
    print(flag)

아래의 질문을 얼마나 답할 수 있는지 체크해보세요!
1. 파이썬 코드를 보고, 프로그램이 어떤 흐름으로 실행될 지 예상할 수 있다.
2. 비트 연산 중 AND, OR, XOR이 어떤 연산인지 설명할 수 있고, bit_quiz1을 풀 수 있다.
3. SHIFT 연산을 할 때 숫자의 비트들이 어떻게 바뀌는지 설명할 수 있고, bit_quiz2을 풀 수 있다.
4. 모듈로 연산이 무엇인지 설명할 수 있다.
5. 모듈로 연산에서 덧셈, 뺄셈, 곱셈이 어떻게 이뤄지는지 설명할 수 있고, modulo_quiz1을 풀 수 있다.
6. 모듈로 연산에서 곱셈 역원이 무엇이고, 나눗셈이 어떻게 이뤄지는지 설명할 수 있다.
7. modulo_quiz2을 풀 수 있다.
8. 모듈로 연산에서 거듭제곱이 어떻게 이뤄지는지 설명할 수 있다.
9. 이때 지수가 매우 클 경우, 어떻게 거듭제곱을 빠르게 계산할 수 있는지 설명할 수 있고, modulo_quiz3을 풀 수 있다.
10. 위 문항 중 체크할 수 있는 답변이 없다.

  • 위 문항들 중 10번만 체크할 수 있다면 우선 파이썬 언어를 먼저 익히셔야 합니다.
  • 1번을 체크할 수 있다면 위의 퀴즈들을 차근차근 하나씩 풀어보며 2~8번 질문의 키워드들을 공부하면 됩니다.

모든 퀴즈들을 해결하고 이해하셨다면, 이제 로드맵의 강의를 들을 준비가 되었습니다.




출처 및 참고자료

https://dreamhack.io/lecture/courses/450

profile
안녕하세요

0개의 댓글