--disable_warnings
drop database if exists MySQLtest1;
drop database if exists MySQLtest2;
drop function if exists f_suid;
--enable_warnings
# Prepare playground
create database MySQLtest1;
create database MySQLtest2;
create user malory@localhost;
grant all privileges on MySQLtest1.* to malory@localhost;
# Create harmless (but SUID!) function
create function f_suid(i int) returns int return 0;
grant execute on function test.f_suid to malory@localhost;
use MySQLtest2;
# Create table in which malory@localhost will be interested but to which
# he won't have any access
create table t1 (i int);
connect (malcon, localhost, malory,,MySQLtest1);
# Correct malory@localhost don't have access to MySQLtest2.t1
--error ER_TABLEACCESS_DENIED_ERROR
select * from MySQLtest2.t1;
# Create function which will allow to exploit security hole
delimiter |;
create function f_evil ()
returns int
sql security invoker
begin
set @a:= current_user();
set @b:= (select count(*) from MySQLtest2.t1);
return 0;
end|
delimiter ;|
# Again correct
--error ER_TABLEACCESS_DENIED_ERROR
select f_evil();
select @a, @b;
# Oops!!! it seems that f_evil() is executed in the context of
# f_suid() definer, so malory@locahost gets all info that he wants
select test.f_suid(f_evil());
select @a, @b;
connection default;
drop user malory@localhost;
drop database MySQLtest1;
drop database MySQLtest2;