{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Documentation.SBV.Examples.Strings.SQLInjection where
import Control.Monad.State
import Control.Monad.Writer
import Data.String
import Data.SBV
import Data.SBV.Control
import Prelude hiding ((++))
import Data.SBV.String ((++))
import qualified Data.SBV.RegExp as R
data SQLExpr = Query SQLExpr
| Const String
| Concat SQLExpr SQLExpr
| ReadVar SQLExpr
instance IsString SQLExpr where
fromString :: String -> SQLExpr
fromString = String -> SQLExpr
Const
type M = StateT (SArray String String) (WriterT [SString] Symbolic)
eval :: SQLExpr -> M SString
eval :: SQLExpr -> M SString
eval (Query SQLExpr
q) = do q' <- SQLExpr -> M SString
eval SQLExpr
q
tell [q']
lift $ lift free_
eval (Const String
str) = SString -> M SString
forall a.
a -> StateT (SArray String String) (WriterT [SString] Symbolic) a
forall (m :: * -> *) a. Monad m => a -> m a
return (SString -> M SString) -> SString -> M SString
forall a b. (a -> b) -> a -> b
$ String -> SString
forall a. SymVal a => a -> SBV a
literal String
str
eval (Concat SQLExpr
e1 SQLExpr
e2) = SString -> SString -> SString
(++) (SString -> SString -> SString)
-> M SString
-> StateT
(SArray String String)
(WriterT [SString] Symbolic)
(SString -> SString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExpr -> M SString
eval SQLExpr
e1 StateT
(SArray String String)
(WriterT [SString] Symbolic)
(SString -> SString)
-> M SString -> M SString
forall a b.
StateT (SArray String String) (WriterT [SString] Symbolic) (a -> b)
-> StateT (SArray String String) (WriterT [SString] Symbolic) a
-> StateT (SArray String String) (WriterT [SString] Symbolic) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SQLExpr -> M SString
eval SQLExpr
e2
eval (ReadVar SQLExpr
nm) = do n <- SQLExpr -> M SString
eval SQLExpr
nm
arr <- get
return $ readArray arr n
exampleProgram :: SQLExpr
exampleProgram :: SQLExpr
exampleProgram = SQLExpr -> SQLExpr
Query (SQLExpr -> SQLExpr) -> SQLExpr -> SQLExpr
forall a b. (a -> b) -> a -> b
$ (SQLExpr -> SQLExpr -> SQLExpr) -> [SQLExpr] -> SQLExpr
forall a. (a -> a -> a) -> [a] -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 SQLExpr -> SQLExpr -> SQLExpr
Concat [ SQLExpr
"SELECT msg FROM msgs WHERE topicid='"
, SQLExpr -> SQLExpr
ReadVar SQLExpr
"my_topicid"
, SQLExpr
"'"
]
nameRe :: R.RegExp
nameRe :: RegExp
nameRe = Int -> Int -> RegExp -> RegExp
R.Loop Int
1 Int
7 (Char -> Char -> RegExp
R.Range Char
'a' Char
'z')
strRe :: R.RegExp
strRe :: RegExp
strRe = RegExp
"'" RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* Int -> Int -> RegExp -> RegExp
R.Loop Int
1 Int
5 (Char -> Char -> RegExp
R.Range Char
'a' Char
'z' RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
+ RegExp
" ") RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp
"'"
selectRe :: R.RegExp
selectRe :: RegExp
selectRe = RegExp
"SELECT "
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* (RegExp
nameRe RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
+ RegExp
"*")
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp
" FROM "
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp
nameRe
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp -> RegExp
R.Opt ( RegExp
" WHERE "
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp
nameRe
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp
"="
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* (RegExp
nameRe RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
+ RegExp
strRe)
)
dropRe :: R.RegExp
dropRe :: RegExp
dropRe = RegExp
"DROP TABLE " RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* (RegExp
nameRe RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
+ RegExp
strRe)
statementRe :: R.RegExp
statementRe :: RegExp
statementRe = RegExp
selectRe RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
+ RegExp
dropRe
exploitRe :: R.RegExp
exploitRe :: RegExp
exploitRe = RegExp -> RegExp
R.KPlus (RegExp
statementRe RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp
"; ")
RegExp -> RegExp -> RegExp
forall a. Num a => a -> a -> a
* RegExp
"DROP TABLE 'users'"
findInjection :: SQLExpr -> IO String
findInjection :: SQLExpr -> IO String
findInjection SQLExpr
expr = Symbolic String -> IO String
forall a. Symbolic a -> IO a
runSMT (Symbolic String -> IO String) -> Symbolic String -> IO String
forall a b. (a -> b) -> a -> b
$ do
SMTOption -> SymbolicT IO ()
forall (m :: * -> *). SolverContext m => SMTOption -> m ()
setOption (SMTOption -> SymbolicT IO ()) -> SMTOption -> SymbolicT IO ()
forall a b. (a -> b) -> a -> b
$ String -> [String] -> SMTOption
OptionKeyword String
":smt.random_seed" [String
"1"]
badTopic <- String -> Symbolic SString
sString String
"badTopic"
emptyEnv :: SArray String String <- sArray "emptyEnv"
let env = SArray String String -> SString -> SString -> SArray String String
forall key val.
(HasKind key, SymVal key, SymVal val, HasKind val) =>
SArray key val -> SBV key -> SBV val -> SArray key val
writeArray SArray String String
emptyEnv SString
"my_topicid" SString
badTopic
(_, queries) <- runWriterT (evalStateT (eval expr) env)
constrain $ sAny (`R.match` exploitRe) queries
query $ do cs <- checkSat
case cs of
CheckSatResult
Unk -> String -> Query String
forall a. HasCallStack => String -> a
error String
"Solver returned unknown!"
DSat{} -> String -> Query String
forall a. HasCallStack => String -> a
error String
"Solver returned delta-satisfiable!"
CheckSatResult
Unsat -> String -> Query String
forall a. HasCallStack => String -> a
error String
"No exploits are found"
CheckSatResult
Sat -> SString -> Query String
forall a. SymVal a => SBV a -> Query a
getValue SString
badTopic