올해 2026년 DEFCON 34, 즉 34번째 대회에 팀 Cold Fusion 으로 참가해서 4등을 했다.

 

본선 참여를 위해 라스베거스 현지에서 Final을 참여했는데, 이것저것 갔다와서 이것저것 느낀점들, 생각이 바뀐 것들이 좀 있다.

남들에 보여주기 위해서라기 보다는 스스로 일기를 쓴다는 느낌으로 기록을 좀 남겨보고자 한다.

 

마음가짐

사실 LLM 이 뜨면서 블로그 조회수도 떨어지고 하는 이유들로 요즘 블로그 글을 거의 쓰지 않은지 꽤 되었는데, 다시 글을 쓰게 된 것도 바뀐 생각 중 하나이다. 

 

2023년도에 처음 온사이트 데프콘 참석한 이후, 육아 등의 이유로 CTF를 거의 하지 못했고 그래서 이번에 참여하면서도, 이번에 가는것이 큰 의미가 있을까 라는 의구심을 조금 하긴 했었다. 하지만 갔다온 지금으로써는 갔다오길 잘 했다라는 생각이 든다.

 

둘다 다른 관점으로 볼 수 있을 것인데 일단 가기 전에는 안가는쪽으로 생각했던 이유들은 다음과 같다.

 

1. CTF를 적극적으로 하지 않은지 오래되었는데 기여를 할 수 있을까?

2. 어차피 LLM이 다 풀어준다고 하는데 가는게 의미가 있을까?

3. CTF 자체가 이제 의미가 있을까?

4. 11시간 12시간 비행이 고될 것인데 그 가치가 있을까?

5. 비용도 많이 드는데 궂이 라스베가스 현지에 갈 필요가 있을까?

 

지금 다녀와서 생각해봤을때는 조금 바뀐 생각들이 있는데 아래들과 같다.

 

1. 가기로 하면 기여를 하기 위해 준비를 하게 된다. 그 과정에서 성장하게 된다. CTF 문제 풀이에 기여를 못한다면, 인프라 개발이나 다른 부분에서도 기여를 할 수 있다.

2. 어차피 사람간 경쟁이고, 같은 LLM을 쓰더라도 잘 쓰느냐에 따라 결과가 달라질 것이다. 그리고 지금 LLM이 강력해진 상황에서 CTF 상황이 어떤지 확인하는 것 자체에도 의미가 있다.

3. 2와 같다. 어떻게 변할지 확인하는 것에 의미가 있을 수 있다. 그리고 세계 최고의 CTF인 DEFCON에서는 어떻게 대응하는지 알아볼 만 하다.

4. 이건 힘들긴 하다. ㅋㅋㅋ 체력 운동을 평소에 많이 해놓던가 아니면 돈을 많이 벌어서 비즈니스 티켓을 예약해야 겠다. 사실 갈때는 갈만했는데 올때가 정말 힘들었다.

5. 현지 시차에 맞게 대회하는 것이랑, 현장 분위기 느끼고 네트워킹 하고 모티베이션 느끼는 가격이라고 생각된다.

 

이번 온사이트 가는데에 총 경비를 600만원 정도 사용한 것 같은데, 600만원의 가치가 있는 경험인가? 라고 물어봤을때는 가기 전에는 딱히 대답은 하지 못했다.

 

하지만 가기로 결정하기로 한 것은 아래와 같은 질문을 스스로 하면서 였다.

1. 지금 600만원이 없으면 내 인생에 큰 지장이 생기는가? -> No

2. 추후에도 이러한 DEFCON Final을 참석할 기회가 많을 것 같은가? -> No

-> 특히나 내년에 LLM이 더 발달하면 다음 DEFCON CTF의 양상이 많이 달라질 수 있을 것 같았다.

-> 나이가 들어가면서 체력적인 이슈로 점점 더 힘들어지지 않을까?

 

그리고 이러한 경험 등에 가성비를 따지면서 접근하는 사람들이 나를 포함해서 주변에 있었던 것 같은데, 이러한 것에 가성비를 따지지 않고 과감하게 투자하는 사람들이 계속적으로 동기부여도 잘 유지하면서 결국은 결과들이 좋았던 것 같다. 그렇다고 믿고 싶다. 이 분야의 Job을 선택한 이유 자체가 돈을 잘 벌어서가 아닌, 내가 흥미로워하고 재미있어 하는 일이기 때문이다.

 

 

여튼 마음가짐에 대한 이야기들은 여기까지 하고, 대회에 대한 이야기를 좀 해보자

AI 시대에 대응한 DEFCON CTF

올해부터는 DEFCON CTF organizer가 nautilus institute에서 bbb로 바뀌었다. 몇년간 우승을 차지했던 MMM에서 일부가 떨어져 나간 팀으로 알고 있다.

 

새 organizer이기도 하고, 작년 대비 LLM 성능이 많이 좋아졌기 때문에 어떤식으로 대응할지 궁금했다.

 

내가 관찰한 AI 시대에 대응한 DEFCON CTF의 달라진 점들은 아래와 같다.

 

1. flag rebate 라는 rule을 추가가 되었다. pcap을 분석해서 유출된 flag를 찾아서 제출하면 상대방의 공격점수 일부를 되가져올 수 있는 제도이다. pcap 분석 후 flag rebate를 제출하는 일, 그리고 exploit 작성 시 flag가 평문으로 보이지 않도록 하는 일을 추가적으로 해야 해서 CTF 자체에서 해야하는 일을 늘렸다.

 

2. King of the Hill 문제에서 대회 참여자의 LLM 끼리 서로경쟁하게 만들었다. King of the Hill은 결국 참여 팀 간의 스코어 경쟁이다. 한 팀이 LLM으로 좋은 점수를 내면 다른 팀도 LLM으로 좋은 점수를 내야 한다. LLM으로 참여자들의 능력이 augmented 되었어도, 다른 팀도 마찬가지이므로 서로 경쟁하며 변별력을 만든다.

 

3. pipin 문제 처럼 사람이 더 잘 풀수도 있는 문제를 만들었다. 슈퍼마리오와 같은 게임을 만들어놓고, 서로 게임을 풀어서 flag를 탈취하게 만드는 형태이다. 상대방이 게임을 풀기 어렵도록 어려운 위치에서 시작하게 만들어서 방어 한다. LLM도 결국 이를 잘 해내지만, 결국 시간이 얼마나 걸리냐가 문제인 것 같다. 이런 문제에서는 게임에 익숙해진 사람이 오히려 UI를 보면서 실시간 대응으로 문제를 더 잘 풀수도 있다.

 

4. tick이 5분/3분으로 축소되었다. 기존 대회에선 틱이 15분이었던것으로 알고 있는데, 이를 1일차 2일차에는 틱당 5분, 마지막 날에는 틱당 3분으로 축소시켜 대회의 속도감을 많이 높였다. LLM으로 문제는 결국 거의 다 풀수 있지만, 이를 얼마나 남들보다 빨리 푸느냐가 관건이 될 수 있는 부분을 대회의 점수에 반영하도록 했다.

 

여담

packet-manager 개발 관련

이번에 어쩌다 보니, packet-manager 개발을 주도적으로 맡게 되었는데, 생각보다 꽤 큰 크기의 pcap들이 나온다고 하고, 동접자가 수십명은 될 것으로 예상되어 성능 최적화나 UI/UX 개선 등을 똥줄 타면서 이것저것 하게 되었다. 출발하기 전부터 계속 개발을 했는데, 안그래도 육아와 회사 일 등으로 수면시간이 부족한 상태라 AI를 이용해서 어떻게 온몸 비틀기로 개발을 진행했다.

 

성능 및 잠재 이슈들을 고려하면서 여럿이서 쓸 도구를 개발하다보니 나름 신선한 경험이었고, LLM 덕에 개발에 들어가는 시간들을 많이 아꼈지만 여전히 사람이 고려해줘야 하는 것들이 많이 있었다.

 

원하는 기능들을 100% 다 해서 출시하진 못했지만, 대회장에서 사람들이 내가 만든 기능들을 잘 사용해주는 모습들을 보니 꽤나보람있었다. 개발자들이 사용자가 많은 서비스들을 개발하고 maintain하는 것에 보람을 느낀다고 들었는데 이게 이런 기분인가 싶었다.

CTF after party

CTF after party가 매년 있다고 들었었는데 이번에 처음으로 참석 해보았다. 내향인으로써 아는사람도 많지 않아서 좀 부담스럽긴 했는데, 미국까지 온 돈이 아까워서라도 반은 억지로 참석해보았다. 최근 몇년간 별 필요도 없는 영어 공부를 계속 해왔었는데, 덕분에 공부했던 영어를 사용할 수 있어서 좋은 경험이었다. 그리고 언어는 단어 및 문법 연습들도 중요하지만, 결국 이야기 할 경험과, 대화 센스, 자신감 등도 중요하다는 것을 느꼇다.

 

느낀 점

이번에 다녀오면서 느낀 점들이 많긴하다.

20대 초중반 대학생 때 부터 라스베가스를 밥먹듯이 오는 친구들이 꽤 있는 것으로 알고 있는데, 그 친구들이 부러웠다. 이 Cyber Security 도메인을 월드 클래스로 이미 경험하는 것들이 자기네들의 성장과 동기부여 등에 많은 도움이 될 것 으로 보였다. 이 좋은 경험을 더 어린 나이에? 라는 느낌이 들었다.

다음에도 기회가 있을 지는 모르겠지만, 이번에 느낀점들을 원동력 삼아 계속해서 정진해야겠다.

https://stackoverflow.com/questions/22924633/gitignore-is-not-ignoring-directories

 

.gitignore is not ignoring directories

What I did: I think there were some weird configurations from the github gui that caused this issue and prevented me from being able to easily use git from command line or even git-bash. I ended up

stackoverflow.com

Windows에서 개발환경 구축하다가 원인을 모르고 gitignore가 동작하지 않은 경험이 있어서 이를 어떻게 해결했는지 기록으로 남기기로 했다.

 

windows에서 wsl을 사용하지 않고 vscode로 코딩을 하다가, .gitignore파일을 echo "node_modules" >> .gitignore로 생성을 했는데 이것이 문제의 원인이었다.

 

이런식으로 파일을 생성하게 되면 .gitignore 파일의 텍스트 인코딩이 UTF-16LE로 설정되게 되는데, git에서 이 인코딩을 제대로 인식하지 못해서 gitignore에 파일 명을 넣어놔도 git에서 제대로 동작하지 못하는 것이다. 이를 UTF-8 인코딩으로 다시 바꾸어서 저장을 해주면 잘 동작하게 된다.

 

결론: Windows를 Linux처럼 쓰지 말자.

가끔 공부나 일 하기 싫을때 글을 한번씩 쓰려고 하는데, 이번에는 캐시 메모리에 대해 이야기 해보고자 한다. 사실 캐시와 관련된 배경 지식들이 짜잘한 것들이 꽤나 있는데 이것들을 한번에 다 다루려고 하면 글이 길어지고 작성하기도 귀찮아지니, 조금씩 점진적으로 글 내용을 추가해서 작성해보고자 한다.

 

일단 캐시 메모리와 관련된 내용은 컴퓨터공학과 학부 전공 과목 중 컴퓨터구조(Computer Architecture) 과목에서 자세히 다룬다. 하지만 컴퓨터공학을 전공하지 않았거나, 해당 과목을 수강하지 않았거나, 아니면 오래되어서 까먹었다던지 하는 사람들에게 다시 리마인드 시켜주거나 개괄적인 내용을 전달할 수 있는 글을 작성해보고자 한다.

캐시와 버퍼의 차이

캐시(Cache)와 많이 혼용되는 개념 중 버퍼(buffer)라는 것이 있는데, 이 둘은 일단 존재 목적이 다르다. 캐시는 처리속도가 차이나는 장치간의 상호작용 시, 이 처리속도 차이로 인한 성능 저하를 완화시켜 성능을 향상시켜주는 역할을 한다. 캐시와는 달리 버퍼는 데이터를 일시적으로 옮기면서 손실을 방지하는 보조 장치 같은 느낌이다.

 

이렇게만 설명하면 아직 감이 잘 안올 것인데, 간단한 예시를 들어보겠다. 부대찌게 식당에 가서 큰 부대찌게 그릇이 있고, 내 앞에는 앞접시가 있다. 보통 이 앞접시에 부대찌개를 일정량 덜어서 먹을 것인데, 부대찌게 그릇이 주 메모리(Main Memory)이고, 앞접시가 캐시 메모리(Cache Memory)라고 볼 수 있다.

 

이때 중요한 점은, 큰 부대찌게 그릇은 양이 크고, 앞접시는 양이 작다. 그리고 밥 먹는 사람이 부대찌개 그릇까지 수저를 가져가서 음식을 퍼나르는데는 시간이 많이 걸리고, 앞접시까지 수저를 가져가는 데에는 시간이 조금 걸린다. 부대찌게 그릇까지 닿는데에 100초가 걸리고, 앞접시 까지 닿는데에 1초가 걸린다면 이는 실제 주 메모리와 캐시 메모리와의 관계가 비슷해진다. (주 메모리와 캐시 메모리의 읽기쓰기의 속도 차이는 100배 정도라고 한다). 여기 까지 이해했으면 여러분은 캐시 메모리에 대해 어느정도 감을 잡았을 것이다.

 

그러면 버퍼에 대한 예시도 한번 들어보자.

여러분들은 C 언어 에서 swap 함수에 대해 한번들어본 적이 있을 것이다. swap 함수는 인자로 들어온 2개의 변수의 값을 서로 바꾸어 주는 역할을 한다.

void swap(int& a, int& b) {
    int buffer = a;
    a = b;
    b = tmp;
}

자 위에 코드에서 여러분은 이제 버퍼가 뭔지 알게 되었다. a변수와 b변수의 값을 서로 바꿀 때, 그냥 대입(assignment)를 하면 기존의 a변수에 있던 값을 잃어버리게 되므로, 이 값을 임시로 저장할 공간이 필요하다. 그래서 임시 지역 변수를 하나 선언하는데 이 변수의 이름이 buffer이고 실제로 버퍼이다. 보통 버퍼라 하면 배열 형태로 된 것들만 생각하지만, 용도만 생각해 보면 이러한 변수도 버퍼가 된다. 

메모리 계층구조 구성요소들

자 이제 캐시가 왜 필요한지 구체적으로 알아보자.

컴퓨터는 다양한 장치들로 구성되어 있지만 가장 필수적인 두가지를 한번 고르자면 CPU와 주 메모리(Main memory)이다. CPU는 기계어 Instruction 들을 fetch 해서 실행하고, 이 기계어 내용들에 따른 적절한 연산을 수행한다. 이 연산들은 대부분 레지스터나 메모리에서 값을 읽거나 쓰고, 산술연산을 한다.

레지스터

레지스터는 CPU에 있는 매우 작은 메모리 여럿이며, CPU 아키텍쳐에 따라 레지스터의 크기, 종류와 개수가 다르다. 범용 레지스터의 크기에 따라 CPU bit를 정의한다.

 즉 32bit CPU는 범용 레지스터의 크기가 32bit이며, 64bit CPU는 범용 레지스터의 크기가 64bit이다. 그리고 이 때 범용 레지스터의 크기를 CPU의 워드 사이즈라고 하며, 이 워드 사이즈는 CPU가 기본적으로 처리하는 데이터의 크기이다.

 

그리고 레지스터의 경우 한번에 처리할 수 있는 데이터의 양이 수십 비트 밖에 안되는 매우 작은 메모리 이지만, 그 처리 속도는 매우 빠르다.

주 메모리

하지만 이렇게 적은 양의 데이터만 처리할 수는 없기 때문에 큰 크기의 메모리가 필요한데, 이것이 주 메모리, 우리가 흔히 말하는 램(RAM: Random Access Memory)라고 부르는 장치이다. 요즘 사용하는 주 메모리의 크기는 다양한데 대부분 8GB이상의 주 메모리를 사용하는 것 같다. 주 메모리는 레지스터를 구성하는 메모리 반도체 소자보다 값싼 소자 및 회로를 사용하므로 레지스터보다 큰 용량 대비 가격이 저렴하지만, 읽기 쓰기 속도가 100배 이상 차이날 정도로 성능 차이가 심하다.

CPU 레지스터와 주 메모리의 처리 속도 차리

CPU에서 연산을 하려면 주 메모리에서 레지스터로 값을 가져와야 하는데, 레지스터와 주 메모리의 읽기 쓰기(I/O) 속도 차이가 심하므로 주 메모리에서 극심한 병목 현상을 겪게 된다. CPU는 빠른 처리 속도를 가지고 있지만, 주 메모리에서 값을 받아오는 것을 기다리다 보니 처리 속도가 주 메모리의 속도로 하향 평준화가 되는 것이다.

캐시 메모리

첫 단락에서 언급 했듯, 이러한 레지스터와 주 메모리의 I/O 성능 차이를 완화시키기 위해 레지스터와 주 메모리 사이에 캐시 메모리를 도입했다. 캐시 메모리는 레지스터 보다는 메모리 사이즈가 더 크고, 주 메모리 보다는 메모리 사이즈가 작다. 그리고 레지스터와 같이 고속으로 처리할 수 있는 비싼 반도체 소자를 활용해서 빠른 읽기 쓰기가 가능하도록 제작하였다.

캐시 메모리의 동작 예시

그래서 이 캐시 메모리는 부대찌개의 앞접시 처럼, 레지스터가 바로 주 메모리로 접근 하는 것이 아닌 고속의 캐시 메모리에 데이터를 덜어서 가져가도록 하는 것이다. 캐시 메모리는 다음과 같은 시나리오로 동작한다.

  1. CPU가 1번 주소의 메모리 값을 읽어오고자 한다.
  2. CPU는 캐시 메모리를 참조해서 1번 주소의 데이터 값이 있는지 확인한다.
  3. 캐시 메모리에 1번 주소의 데이터가 값이 없다 = 이를 캐시 미스(Cache miss)라고 한다.
  4. 주 메모리의 1번 주소의 데이터 값을 Copy해서 캐시에 저장한다.
  5. CPU가 1번 주소의 메모리 값을 다시 읽어오고자 한다.
  6. CPU는 캐시 메모리를 참조해서 1번 주소의 데이터 값이 있는지 확인한다.
  7. 캐시 메모리는 1번 주소의 데이터가 값이 있다. = 이를 캐시 히트(Cache hit)라고 한다.
  8. CPU는 느린 주 메모리 까지 접근할 필요 없니 캐시 메모리에서 1번 주소의 메모리 값을 읽어온다.

주 메모리에 접근할 때 100의 latency가 발생하고, 캐시 메모리에 접근할 때 1의 latency가 발생한다고 했을 때, 같은 값을 한번 불러서 여러번 읽는다면 굉장한 속도 효율을 낼 수 있다.

비슷하게 메모리에 값을 쓰는(Write) 경우도 캐시 메모리에만 값을 쓰는 방식으로 latency를 줄일 수 있다. 그러면 일시적으로 캐시 메모리의 데이터 값과, 주 메모리의 데이터 값이 다른 Memory inconsistency가 발생할 수 있는데 캐시 메모리에서 해당 주소값이 캐시 메모리에서 나가게 되는 경우 주 메모리에 바뀐 내용을 Write하게 된다. 이 정책을 Write back이라고 한다.

이와 달리 Write through라는 정책을 활용하면 메모리에 값을 써야 할 때, 캐시 메모리 뿐만 아니라 주 메모리에도 값을 쓴다. 이렇게 하면 Memory inconsistency가 발생하지 않지만 성능적으로는 느려진다.

스토리지

앞서 이야기한 레지스터, 캐시 메모리, 주 메모리는 전기의 공급이 끊기면 데이터가 사라지는 기억장치들이다. 이와 달리 전기의 공급이 끊겨도 데이터가 남아있는 보조기억장치가 있는데 주로 이들을 스토리지(Storage)라고 부르겠다. 우리에게 친숙한 하드디스크나 SSD와 같은 친구들이 이 스토리지에 해당한다. 이 스토리지는 용량 대비 가격이 매우 싸서 많은 데이터를 저장하기 적합하지만, 읽기 쓰기 속도는 주 메모리 보다도 더 느리다.

메모리 계층구조(Memory Hierarchy)

위에서 언급한 저장장치들을 피라미드 형태로 그려놓은 이미지이다. 직접 그리려고 했는데 너무 귀찮은 나머지 위키에서 퍼왔다.

위와 같은 형태를 메모리 계층구조라고 한다. 아래로 갈 수록 저장 공간의 크기가 커진다. 그 이유는 단위 용량 당 메모리 반도체 & 소자 & 구성요소의 가격이 값싸서 그렇다. 그리고 위쪽으로 갈 수록 처리속도가 빨라진다.

이러한 메모리 계층구조를 갖는 컴퓨터를 만드는 이유는 가성비 때문이다. 사실 캐시메모리 등을 구성하는 정적 메모리 소자로만 주 메모리를 만들면 굉장히 빠른 고성능의 컴퓨터를 쓸 수 있지만, 가격이 기하급수적으로 비싸지기 때문에 그렇게 하지않는다. 그렇다고 캐시 메모리를 빼서 가격을 낮추면 처리 속도가 너무 느려진다.

그런데 메모리가 위쪽으로 올라갈 수록 크기가 급격히 작아지는데 이러한 계층구조로 생각보다 꽤 괜찮은 가성비의 속도가 나오는 이유가 무엇일까? 이 이유로는 지역성이라는 소프트웨어의 특징 덕분이다.

지역성(Locality)

메모리 계층구조가 가성비를 가질 수 있는 이유로는 지역성이라는 소프트웨어, 컴퓨터 프로그램의 특징 덕분이다. 그리고 이 지역성은 시간 지역성(Temporal Locality)와 공간 지역성(Spatial Locality)로 나뉜다.

 

간단하게 이야기 하자면, 시간 지역성은 지금 1번지 메모리에 접근된 적이 있으면 가까운 시일 내에도 1번지 메모리에 접근될 확률이 높다는 것이다. 공간 지역성은 비슷하게 1번지 메모리에 접근된 적이 있으면 2,3번지 메모리에 접근될 확률이 높다는 것이다.

 

왜 그럴까?

우리가 프로그램을 작성하는 방식을 한번 생각해보자.

 

만약 CPU가 함수안의 코드를 실행한다고 하면, 그 함수 안에서는 함수의 인자와 지역변수들을 많이 활용할 것이다. 이것이 일단 시간 지역성이다. 그리고 for-loop와 같은 반복문의 경우 반복문 instruction 들이 계속 반복되므로 이 경우에도 시간 지역성이 만족된다.

그리고 배열같은 경우도 반복문으로 배열 순회를 한다던지 하면 arr[1]에 접근된 뒤 곧이어 arr[2]를 접근할 확률이 높을 것이다. 이러한 이유로 공간 지역성도 만족된다.

 

따라서 이런 특성 때문에 캐시 메모리와 같이 매우 작은 크기로도 꽤 괜찮은 성능 향상을 이룰 수 있다. 그리고 이런 지역성을 고려해서 캐시 메모리의 스케쥴링도 설계되어 있다.

 

LRU Cache scheduling 알고리즘과 Cache block size와 같은 내용들을 추가적으로 언급해야 하는데, 나중에 다시 써 보도록 하겠다.

레퍼런스

https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EB%A6%AC_%EA%B3%84%EC%B8%B5_%EA%B5%AC%EC%A1%B0

+ Recent posts