Category Archives: Wordpress

Login to WordPress from Python

I’ve been trying to learn some Python and have been tinkering with the requests module. Here is how I am able to log into a webpage, such as WordPress.

import requests
 url = ""
 redirect_to  = ""
 with requests.Session() as session:
     post =, data={
         'log': 'admin',
         'pwd': 'password',
         'redirect_to': redirect_to
         }, allow_redirects=True)
     get = session.get(redirect_to, cookies=post.cookies)

WordPress TwentyTwenty Theme – Inter font Apache2 error

I’m testing out the development version of TwentyTwenty theme from WordPress on this site.

I noted that calls to /assets/fonts/inter/Inter-upright.var.woff2 were causing some grief for Apache2 (Error 500):

AH00681: Syntax error in type map, no ':' in /var/www/clients/client0/web1/web/wp-content/themes/twentytwenty/assets/fonts/inter/Inter-upright.var.woff2 for header wof2

Cursory Google search indicates that Apache2 is interpreting filenames with .var.* in the name as a Type Map.

To work around this, I’ve set the following in my .htaccess:

RemoveHandler .var

WP Preserve Backslashes

I created a WordPress plugin based on a personal dilemma I ran into with my site being stripped of backslashes.

Upon post save, it converts backslashes to HTML entity ] which is what will be stored in the database.

The plugin is available on GitHub at
Here’s the function if you want to just drop it in your functions.php file instead of installing it as a plugin.

function wppb_keepbackslash($PostID) {
    $thePost = get_post($PostID);
    $Content = str_replace('\\', '\', $thePost->post_content);
    // unhook this function so it doesn't loop infinitely
    remove_action( 'save_post', 'wppb_keepbackslash' );
    $UpdatedPost = array (
          'ID'           => $PostID,
          'post_title'   => $thePost->post_title,
          'post_content' => $Content
  wp_update_post( $UpdatedPost );
/** if (is_wp_error($post_id)) {
 $post_id=   wp_update_post( $UpdatedPost );
        $errors = $post_id->get_error_messages();
        foreach ($errors as $error) {
                echo $error;
}   **/
    // re-hook this function
    add_action( 'save_post', 'wppb_keepbackslash' );
add_action('save_post', 'wppb_keepbackslash' ); // Update Content when saving content

All my backslashes are gone in WordPress. Yikes.

Discovered that my most recent conversion from SQLite to MySQL seems to have screwed up my backslashes in all my posts that have backslashes.

This is bad because my code snippets should not be copy & pasted and run at face value unless you verify the code!  It could seriously break shit.

Ugh.  This is going to be a PITA to go and fix 500 posts.   It might be quicker to try to fix the SQLite DB file and try another conversion.  This isn’t the first time I’ve noticed this problem.  I see the issue when I restore from XML files as well, and even just copying database using something like mysqldump to dump and then importing using mysql command.  I’m probably just missing a simple flag to not strip slashes or something.

My next step is to confirm if there is actually a backslash in the SQL data and it is just being stripped in the_content() or something;  or if the backslash is REALLY not there.  *sad face*

update 1:  the slashes are not in SQL.  Looks like I need to look at my export DB to see if they are in that. *crosses fingers*

update 2:  found this article that creates a function to convert backslashes into HTML entities as the posts are saved.

WordPress SQLite to MySQL Migration Complete

Just finished migrating my website from SQLite to MySQL. What a rush. (lol)

It was actually not as bad as I thought. A lot of sed, grep and other sorcery involved; especially in transforming of SQLite statements to MySQL.

Some quick commands I used:

sqlite techish.db .dump > production_2018-08-23.dump.sql

I found that it used quotes for tables and column names, so I had to remove those first and foremost.

sed -i '/INSERT INTO/,/VALUES (/s/"//g' production_2018-08-23.dump.sql

Next I found that there was an error using mysql -ufoo -p mynewdatabase < production_2018-08-23.dump.sql because the table creations were failing still. So I did a quick fresh install of a vanilla WordPress install, did a dump of the database and just grabbed the table creation parts out:

Dump fresh database:

mysqldump -ufoo -p wordpres > wordpress.sql

Next, I just want table creations…

awk '/CREATE TABLE/, /) ENGINE/' wordpress.sql > create_tables.sql

Next, run create_tables.sql on my new database and then import data.

mysql -ufoo -p mynewdatabase < create_tables.sql

Sweet, that worked and I have a baseline of tables now.

Now importing the data…

WordPress Unauthorized Password Reset Vulnerability (CVE-2017-8259)

WordPress has a password reset feature that contains a vulnerability which might in some cases allow attackers to get hold of the password reset link without previous authentication.
Such attack could lead to an attacker gaining unauthorized access to a victim’s WordPress account.  This affects all versions of WordPress, including the current version, 4.7.4.


The vulnerability stems from WordPress using untrusted data by default when creating a password reset e-mail that is supposed to be delivered only to the e-mail associated with the owner’s account.

This can be observed in the following code snippet that creates a From email header before calling a PHP mail() function:


if ( !isset( $from_email ) ) {
        // Get the site domain and get rid of www.
        $sitename = strtolower( $_SERVER['SERVER_NAME'] );
        if ( substr( $sitename, 0, 4 ) == 'www.' ) {
                $sitename = substr( $sitename, 4 );
        $from_email = 'wordpress@' . $sitename;

3 separate example scenarios (both the ones that require victim interaction and those that do not) include:

  1. Attacker can perform a prior DoS attack on the victim’s email account/server (e.g by sending multiple large files to exceed user’s disk quota, attacking the DNS server etc) in order to prevent the password reset email from reaching the victim’s account and bounce back to the malicious sender address that is pointed at the attacker (no user interaction required)
  2. Some autoresponders might attach a copy of the email sent in the body of the auto-replied message (no user interaction required)
  3. Sending multiple password reset emails to force the user to reply to the message to inquiry explanation for endless password reset emails. The reply containing the password link would then be sent to attacker. (user interaction required)


  1. If you are using Apache, you can turn on UseCanonicalName (see:
  2. I created a simple plugin that you can install in your WordPress installation. It will disable the last password functionality.
    Disable Password Reset

From Linux Install to WordPress

A typical installation of Debian 8.x (Jessie) precedes this where I only select base system and ssh server options during operating system installation.  After installation, this is a typical configuration to get me up and running. These are my notes.
Debian Customization
These are customizations to suit my taste.

apt-get update && apt-get upgrade
dpkg-reconfigure dash
echo UseDNS no >>/etc/ssh/sshd_config && /etc/init.d/ssh restart
apt-get install fail2ban vim-nox unzip

Webserver Installation: nginx

apt-key add nginx_signing.key
echo 'deb jessie nginx' >> /etc/apt/sources.list
echo 'deb-src jessie nginx' >> /etc/apt/sources.list
apt-get update && apt-get install nginx

nginx Configuration

There are a few customizations I make and I have scripted most of this since it’s repetitive.

In the first line below, worker_processes 2; is derived from grep 'cpu cores' /proc/cpuinfo | head -1
sed -i 's/user[ ]*nginx/user www-data/g; s/worker_processes[ ]*1/worker_processes 2/g' /etc/nginx/nginx.conf
sed -i 's/access_log.*;/access_log off;/g' /etc/nginx/nginx.conf
sed -i '/access_log off;/a client_max_body_size 12m;' /etc/nginx/nginx.conf
/etc/init.d/nginx restart

With basic configuration changes made to nginx.conf, I now focus on creating the site configuration.

rm /etc/nginx/conf.d/*
cat <<EOF >>/etc/nginx/conf.d/`hostname`.conf
server {
        listen 80;
        root /var/www;
        index index.php index.html index.htm;
        location / {
                try_files $uri $uri/ /index.php;
        location ~ .php$ {
                try_files $uri =404;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    location ~ /.well-known {
                allow all;

That concludes nginx installation and configuration to THIS point. I’ll revisit toward the end when I implement SSL.

PHP 7 Installation

I use dotdeb to install PHP 7.0 via apt-get.

echo 'deb jessie all' >> /etc/apt/sources.list
echo 'deb-src jessie all' >> /etc/apt/sources.list
apt-key add dotdeb.gpg
apt-get update
apt-get install php7.0-fpm php7.0-mysql php7.0-gd php7.0-mcrypt

PHP 7 Configuration

With PHP7 installation completed, I make a few changes.

sed -i 's/^upload_max_filesize.*/upload_max_filesize = 10m/g; s/^allow_url_fopen.*/allow_url_fopen = Off/g; s/^post_max_size.*/post_max_size = 12m/g' /etc/php/7.0/fpm/php.ini

MySQL (MariaDB) Installation

Installing MariaDB is pretty straight forward with only a minor tweak in the configuration at the end.

apt-get install mariadb-server

MariaDB Configuration

sed -i 's/^bind-address/#bind-address/g; /^#bind-address/a skip-networking' /etc/mysql/my.cnf

Restart Services & Test

Restart the services and test out things to make sure everything works.

systemctl restart nginx.service
systemctl restart php7-fpm.service
systemctl restart mysql.service

WordPress Installation

WordPress installation is straight forward.

Database Preparation

Make sure to substitute wordpress, wpuser and ... below to reflect your database, database username and database user password.
cd /var/www
mysql -uroot -p -e create database wordpress; grant all on wordpress.* to 'wpuser'@'%' identified by '...'; flush privileges

WordPress Download & Extract

mv wordpress/* .
rm -rf wordpress/; rm
chown www-data.www-data -R .

WordPress Configuration

At this point, the database is ready to go and I just visit my website to finish the WordPress installation via Web interface.

Let’s Encrypt SSL Certificate


openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

Add Jessie backports repo and install.

echo 'deb jessie-backports main' >>/etc/apt/sources.list
apt-get update
apt-get install certbot -t jessie-backports

Certificate Generation

I generate a certificate for my top level domain and subdomain.

certbot certonly --webroot -w /var/www -d
certbot certonly --webroot -w /var/www -d

nginx SSL Configuration

Create a directory in /etc/nginx to store a few snippets of nginx configuration.

mkdir /etc/nginx/ssl

Create SSL parameters configuration file, ssl-params.conf, that we’ll call in our site configuration file.

cat <<EOF >>/etc/nginx/ssl/ssl-params.conf
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver valid=300s;
    resolver_timeout 5s;
    add_header Strict-Transport-Security max-age=63072000; includeSubdomains; preload;
    add_header Strict-Transport-Security max-age=63072000; includeSubdomains;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

Create a configuration file, ssl-kreider.conf, that will reference where my top level domain SSL certificate is stored. I call this file from main nginx site configuration file later.

cat <<EOF >>/etc/nginx/ssl/ssl-kreider.conf
ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;

Create a configuration file, ssl-www-kreider.conf, that will reference where my www subdomain SSL certificate is stored. I call this file from main nginx site configuration file later.

cat <<EOF >>/etc/nginx/ssl/ssl-www-kreider.conf
ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;

I update my main site’s nginx configuration.

Note that `hostname` will expand the system hostname automatically. Replace `hostname`.conf (including backticks) with your configuration file name as applicable.
vim /etc/nginx/conf.d/`hostname`.conf

My finalized site configuration file.

server {
        listen 80;
        return 307$request_uri;
server {
        listen 443 ssl;
        include ssl/ssl-kreider.conf;
        include ssl/ssl-params.conf;
        root /var/www;
        index index.php index.html index.htm;
        location / {
                try_files $uri $uri/ /index.php;
        location ~ .php$ {
                try_files $uri =404;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name                                                                                                                               ;
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

Change WordPress Password using MySQL

If the Forgot password option doesn’t work for you (which it really should), then you can change the password of your self-hosted WordPress login.

Check the contents of wp-config.php for the database login. Use that login information for MySQL login.

define('DB_NAME', 'wpdatabase');
define('DB_USER', 'wpdbuser');
define('DB_PASSWORD', 'wpdbpassword');

Replacing DB_USER and DB_NAME from that command with the values from wp-config.php, enter the following command to connect to the database on your server.

mysql -uDB_USER -p DB_NAME

Get a list of users in the WordPress database.

select user_login from wp_users;

This might return multiple entries, but it was just one in my case: admin
To change the password, issue the following. Be sure to change ‘admin’ to the value of your user_login.

update wp_users set user_pass=md5('NEW_PASSWORD_HERE') where user_login='admin';

All done. Now you can log into your WordPress administration panel with the new password you specified.