mysql: logging specific user logins - mysql

I'm looking for a stored procedure or something to log user-logins to mysql.
Users will have to be in a positive list.
I need to log user, login time, ip/hostname and query ( query not essential, but a nice-to-have).
My major problem is that I cannot do it via general query log, as is generates +23Gb of log per instance/day (i have 18 instances) and I don't have capacity for it.
I was thinking somewhat in lines of stored procedures, but cannot get my head around it, but not sure if it is the right way to go about it.
Capacity expansion is not an option.
Platform is:
CentOS Linux release 7.3.1611
mysql-community-server-5.6.36-2
Any thoughts on how to solve my issue are welcome.

You may be looking for the mysql init_connect system variable :
A string to be executed by the server for each client that connects. The string consists of one or more SQL statements, separated by semicolon characters.
So basically you want to create a table where logins will be logged, and set the init_connect system variable to something like :
INSERT INTO my_logging_audit
SELECT USER(), NOW()
WHERE USER() IN ('foo', 'bar');

Related

Get user info with trigger in MySQL

As the title said, I'd like to get some information of the user who is executing the SQL statement in MySQL. For example, if some SQL statement is executed by a user called 'work', how could I got the user name and host with trigger before it is actually executed in MySQL.
Actually, I just want to monitor some actions, like DELETE, in MySQL. And I'm not sure the binlog can record the username and host who executed the SQL statement.
SELECT * FROM information_schema.PROCESSLIST WHERE USER="someuser";
Now it is up to you what you use. Write a cron job (linux) to store it into a file or write a MySQL event to enter it into a MySQL table.
The user#host that you appear to be looking for is available from the USER() information function.
https://dev.mysql.com/doc/refman/5.6/en/information-functions.html#function_user

What is this SQL injection doing?

Long story short, through an old asp site I run someone found an unfiltered URL parameter and was able to run this query. I'm trying to figure out what it DOES though...
The query should read:
select * from reserve where id = 345
the one that was ran was:
select * from reserve where id = 345 and ascii(substring((select concat(user,0x3a,password,0x3a,host) from mysql.user limit 0,1),17,1))=53
I'm really not sure what this obtains. Any Input?
It might be probing whether or not the web application is accessing the database as root. Removing the ascii(substring()) portions returns the following when run as root:
mysql> select concat(user,0x3a,password,0x3a,host) from mysql.user limit 0,1;
+--------------------------------------+
| concat(user,0x3a,password,0x3a,host) |
+--------------------------------------+
| root:<rootpw-hash>:localhost |
+--------------------------------------+
Following a successful probe, they may then attempt to retrieve the contents of mysql.user from which they can start cracking passwords against rainbow tables.
The second part of where condition is really strange: it looks for a mysql credentials and process them as follows:
concat(user,0x3a,password,0x3a,host) will be something like 'someUser:hisPass:localhost'
the above string will be splitted in a smaller one
the above string is converted to ascii code (you might know it from legacy languages as ord())
the result of the conversion is compared to 53 integer
I suppose that the first part of WHERE statement (id = 345) will always return true while the second one is too specific, so the entire query will probably return an empty result all the time.
the query is seemingly one from the a set of them:
by changing the charcode and substring start position and you can find out all usernames and the corresponding password hashes (when the page renders as expected you have a char match)
it allows to find out that the current user has access to the mysql schema.
An sql injection exploit does not necessarily immediately output the query result to the attackers screen, often the result is only either an error, or no error, or maybe the injection causes a measurable (to the attacker) delay. in that way the attacker can obtain 1 bit of information per request.
By sending lots of requests, iterating over string positions, doing a binary search on the characters - or as in this case a linear search ( which may indicate that the attacker does not really understand what he is doing, but he will get there eventually ), he will be able to find all the characters in the mysql root user passwordhash. ( Which can possibly be bruteforced offline ).
The SQL is trying to read user data from the My-Sql user table which typically contains a list of users and hosts that are allowed to access a given my-sql server.
It looks to me like the perp is trying to trick mysql into dumping the contents of the user table so they can then record the password hashes offline and dcrypt them to find valid logins.
If your web application is using a login that will allow access to the mysql users table, then this is a serious security flaw, if it's using a login that is only granted permission to the tables required for the app then no information will be obtainable.
Security tip: When setting up ANY kind of database it's vitally important that the application using does so with a login/access role that grants it ONLY what it needs.
If your application only ever needs to read data and never modify it, then it should never have any permissions other than to read. You always need to double check this, because most database systems will by default create user roles for a given database with full read, create, modify privileges.
Always create a specific user, just for that db and or collection of tables, and always give that user the absolute minimum that's required, if your app does then get hacked with a cross site scripting attack, the most their going to get access too is that one specific database.

MySQL: automatic mapping of UTC to Local Timezone

I have a forum where users can post comments. When a comment is created its corresponding datetime value is stored in UTC format.
I intend to present the data in local time, say 'ASIA/SINGAPORE';
2 options:
use convert_tz each time querying the database. I dont like the
approach, cause it makes me rewrite the select_expr each time querying.
use SET time_zone = 'ASIA/SINGAPORE';
As for the second option, I want to know what is the validity scope of the command (no super privilege here). more specifically, say if i'm using a php application, does the config gets invalid as i close db connection? should i issue the command each time querying the db?
Tnx.
MySQL variables are scoped in the connection (lowest level, between libmysql <-> mysqld). It means, that if PHP itself or some application library uses any kind of mysql connection pooling, then you could observe this variable disappearing (because of invisible connection switching), and the variable definitely will disappear after disconnecting.
If you are not happy rewriting your query, you probably could select apropriate tz name on the fly -- say, form a users table, as long as you have the id of the logged user, like this:
SELECT convert_tz( ..., ..., (select user_tz from users where user_id = ...))

MySQL Injection - Use SELECT query to UPDATE/DELETE

I've got one easy question: say there is a site with a query like:
SELECT id, name, message FROM messages WHERE id = $_GET['q'].
Is there any way to get something updated/deleted in the database (MySQL)? Until now I've never seen an injection that was able to delete/update using a SELECT query, so, is it even possible?
Before directly answering the question, it's worth noting that even if all an attacker can do is read data that he shouldn't be able to, that's usually still really bad. Consider that by using JOINs and SELECTing from system tables (like mysql.innodb_table_stats), an attacker who starts with a SELECT injection and no other knowledge of your database can map your schema and then exfiltrate the entirety of the data that you have in MySQL. For the vast majority of databases and applications, that already represents a catastrophic security hole.
But to answer the question directly: there are a few ways that I know of by which injection into a MySQL SELECT can be used to modify data. Fortunately, they all require reasonably unusual circumstances to be possible. All example injections below are given relative to the example injectable query from the question:
SELECT id, name, message FROM messages WHERE id = $_GET['q']
1. "Stacked" or "batched" queries.
The classic injection technique of just putting an entire other statement after the one being injected into. As suggested in another answer here, you could set $_GET['q'] to 1; DELETE FROM users; -- so that the query forms two statements which get executed consecutively, the second of which deletes everything in the users table.
In mitigation
Most MySQL connectors - notably including PHP's (deprecated) mysql_* and (non-deprecated) mysqli_* functions - don't support stacked or batched queries at all, so this kind of attack just plain doesn't work. However, some do - notably including PHP's PDO connector (although the support can be disabled to increase security).
2. Exploiting user-defined functions
Functions can be called from a SELECT, and can alter data. If a data-altering function has been created in the database, you could make the SELECT call it, for instance by passing 0 OR SOME_FUNCTION_NAME() as the value of $_GET['q'].
In mitigation
Most databases don't contain any user-defined functions - let alone data-altering ones - and so offer no opportunity at all to perform this sort of exploit.
3. Writing to files
As described in Muhaimin Dzulfakar's (somewhat presumptuously named) paper Advanced MySQL Exploitation, you can use INTO OUTFILE or INTO DUMPFILE clauses on a MySQL select to dump the result into a file. Since, by using a UNION, any arbitrary result can be SELECTed, this allows writing new files with arbitrary content at any location that the user running mysqld can access. Conceivably this can be exploited not merely to modify data in the MySQL database, but to get shell access to the server on which it is running - for instance, by writing a PHP script to the webroot and then making a request to it, if the MySQL server is co-hosted with a PHP server.
In mitigation
Lots of factors reduce the practical exploitability of this otherwise impressive-sounding attack:
MySQL will never let you use INTO OUTFILE or INTO DUMPFILE to overwrite an existing file, nor write to a folder that doesn't exist. This prevents attacks like creating a .ssh folder with a private key in the mysql user's home directory and then SSHing in, or overwriting the mysqld binary itself with a malicious version and waiting for a server restart.
Any halfway decent installation package will set up a special user (typically named mysql) to run mysqld, and give that user only very limited permissions. As such, it shouldn't be able to write to most locations on the file system - and certainly shouldn't ordinarily be able to do things like write to a web application's webroot.
Modern installations of MySQL come with --secure-file-priv set by default, preventing MySQL from writing to anywhere other than a designated data import/export directory and thereby rendering this attack almost completely impotent... unless the owner of the server has deliberately disabled it. Fortunately, nobody would ever just completely disable a security feature like that since that would obviously be - oh wait never mind.
4. Calling the sys_exec() function from lib_mysqludf_sys to run arbitrary shell commands
There's a MySQL extension called lib_mysqludf_sys that - judging from its stars on GitHub and a quick Stack Overflow search - has at least a few hundred users. It adds a function called sys_exec that runs shell commands. As noted in #2, functions can be called from within a SELECT; the implications are hopefully obvious. To quote from the source, this function "can be a security hazard".
In mitigation
Most systems don't have this extension installed.
If you say you use mysql_query that doesn't support multiple queries, you cannot directly add DELETE/UPDATE/INSERT, but it's possible to modify data under some circumstances. For example, let's say you have the following function
DELIMITER //
CREATE DEFINER=`root`#`localhost` FUNCTION `testP`()
RETURNS int(11)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DELETE FROM test2;
return 1;
END //
Now you can call this function in SELECT :
SELECT id, name, message FROM messages WHERE id = NULL OR testP()
(id = NULL - always NULL(FALSE), so testP() always gets executed.
It depends on the DBMS connector you are using. Most of the time your scenario should not be possible, but under certain circumstances it could work. For further details you should take a look at chapter 4 and 5 from the Blackhat-Paper Advanced MySQL Exploitation.
Yes it's possible.
$_GET['q'] would hold 1; DELETE FROM users; --
SELECT id, name, message FROM messages WHERE id = 1; DELETE FROM users; -- whatever here');

Copying contents of a MySQL table to a table in another (local) database

I have two MySQL databases for my site - one is for a production environment and the other, much smaller, is for a testing/development environment. Both have identical schemas (except when I am testing something I intend to change, of course). A small number of the tables are for internationalisation purposes:
TransLanguage - non-English languages
TransModule - modules (bundles of phrases for translation, that can be loaded individually by PHP scripts)
TransPhrase - individual phrases, in English, for potential translation
TranslatedPhrase - translations of phrases that are submitted by volunteers
ChosenTranslatedPhrase - screened translations of phrases.
The volunteers who do translation are all working on the production site, as they are regular users.
I wanted to create a stored procedure that could be used to synchronise the contents of four of these tables - TransLanguage, TransModule, TransPhrase and ChosenTranslatedPhrase - from the production database to the testing database, so as to keep the test environment up-to-date and prevent "unknown phrase" errors from being in the way while testing. My first effort was to create the following procedure in the test database:
CREATE PROCEDURE `SynchroniseTranslations` ()
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
BEGIN
DELETE FROM `TransLanguage`;
DELETE FROM `TransModule`;
INSERT INTO `TransLanguage` SELECT * FROM `PRODUCTION_DB`.`TransLanguage`;
INSERT INTO `TransModule` SELECT * FROM `PRODUCTION_DB`.`TransModule`;
INSERT INTO `TransPhrase` SELECT * FROM `PRODUCTION_DB`.`TransPhrase`;
INSERT INTO `ChosenTranslatedPhrase` SELECT * FROM `PRODUCTION_DB`.`ChosenTranslatedPhrase`;
END
When I try to run this, I get an error message: "SELECT command denied to user 'username'#'localhost' for table 'TransLanguage'". I also tried to create the procedure to work the other way around (that is, to exist as part of the data dictionary for the production database rather than the test database). If I do it that, way, I get an identical message except it tells me I'm denied the DELETE command rather than SELECT.
I have made sure that my user has INSERT, DELETE, SELECT, UPDATE and CREATE ROUTINE privileges on both databases. However, it seems as though MySQL is reluctant to let this user exercise its privileges on both databases at the same time. How come, and is there a way around this?
The answer to this question is extremely simple, disproportionately to the amount of time I spent typing it up. My problem was merely a case-sensitivity issue. That's right, I capitalised in the names of the databases where I should not have. MySQL's error messages, stating that I was denied permission to carry out the commands I had issued rather than informing me that the databases I was trying to access did not exist, misled me as to the nature of the problem. I leave the question up in case it is somehow instructive to someone, somewhere.