{-# 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 SString
q' <- SQLExpr -> M SString
eval SQLExpr
q
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [SString
q']
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall a b. (a -> b) -> a -> b
$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall a. SymVal a => Symbolic (SBV a)
sbvExists_
eval (Const String
str) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. SymVal a => a -> SBV a
literal String
str
eval (Concat SQLExpr
e1 SQLExpr
e2) = SString -> SString -> SString
(++) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExpr -> M SString
eval SQLExpr
e1 forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SQLExpr -> M SString
eval SQLExpr
e2
eval (ReadVar SQLExpr
nm) = do SString
n <- SQLExpr -> M SString
eval SQLExpr
nm
SArray String String
arr <- forall s (m :: * -> *). MonadState s m => m s
get
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (array :: * -> * -> *) a b.
SymArray array =>
array a b -> SBV a -> SBV b
readArray SArray String String
arr SString
n
exampleProgram :: SQLExpr
exampleProgram :: SQLExpr
exampleProgram = SQLExpr -> SQLExpr
Query forall a b. (a -> b) -> a -> b
$ 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
"'" 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' forall a. Num a => a -> a -> a
+ RegExp
" ") forall a. Num a => a -> a -> a
* RegExp
"'"
selectRe :: R.RegExp
selectRe :: RegExp
selectRe = RegExp
"SELECT "
forall a. Num a => a -> a -> a
* (RegExp
nameRe forall a. Num a => a -> a -> a
+ RegExp
"*")
forall a. Num a => a -> a -> a
* RegExp
" FROM "
forall a. Num a => a -> a -> a
* RegExp
nameRe
forall a. Num a => a -> a -> a
* RegExp -> RegExp
R.Opt ( RegExp
" WHERE "
forall a. Num a => a -> a -> a
* RegExp
nameRe
forall a. Num a => a -> a -> a
* RegExp
"="
forall a. Num a => a -> a -> a
* (RegExp
nameRe forall a. Num a => a -> a -> a
+ RegExp
strRe)
)
dropRe :: R.RegExp
dropRe :: RegExp
dropRe = RegExp
"DROP TABLE " forall a. Num a => a -> a -> a
* (RegExp
nameRe forall a. Num a => a -> a -> a
+ RegExp
strRe)
statementRe :: R.RegExp
statementRe :: RegExp
statementRe = RegExp
selectRe forall a. Num a => a -> a -> a
+ RegExp
dropRe
exploitRe :: R.RegExp
exploitRe :: RegExp
exploitRe = RegExp -> RegExp
R.KPlus (RegExp
statementRe forall a. Num a => a -> a -> a
* RegExp
"; ")
forall a. Num a => a -> a -> a
* RegExp
"DROP TABLE 'users'"
findInjection :: SQLExpr -> IO String
findInjection :: SQLExpr -> IO String
findInjection SQLExpr
expr = forall a. Symbolic a -> IO a
runSMT forall a b. (a -> b) -> a -> b
$ do
forall (m :: * -> *). SolverContext m => SMTOption -> m ()
setOption forall a b. (a -> b) -> a -> b
$ String -> [String] -> SMTOption
OptionKeyword String
":smt.random_seed" [String
"1"]
SString
badTopic <- String -> Symbolic SString
sString String
"badTopic"
SArray String String
emptyEnv :: SArray String String <- forall (array :: * -> * -> *) a b.
(SymArray array, HasKind a, HasKind b) =>
String -> Maybe (SBV b) -> Symbolic (array a b)
newArray String
"emptyEnv" forall a. Maybe a
Nothing
let env :: SArray String String
env = forall (array :: * -> * -> *) b a.
(SymArray array, SymVal b) =>
array a b -> SBV a -> SBV b -> array a b
writeArray SArray String String
emptyEnv SString
"my_topicid" SString
badTopic
(SString
_, [SString]
queries) <- forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
runWriterT (forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT (SQLExpr -> M SString
eval SQLExpr
expr) SArray String String
env)
forall (m :: * -> *). SolverContext m => SBool -> m ()
constrain forall a b. (a -> b) -> a -> b
$ forall a. (a -> SBool) -> [a] -> SBool
sAny (forall a. RegExpMatchable a => a -> RegExp -> SBool
`R.match` RegExp
exploitRe) [SString]
queries
forall a. Query a -> Symbolic a
query forall a b. (a -> b) -> a -> b
$ do CheckSatResult
cs <- Query CheckSatResult
checkSat
case CheckSatResult
cs of
CheckSatResult
Unk -> forall a. HasCallStack => String -> a
error String
"Solver returned unknown!"
DSat{} -> forall a. HasCallStack => String -> a
error String
"Solver returned delta-satisfiable!"
CheckSatResult
Unsat -> forall a. HasCallStack => String -> a
error String
"No exploits are found"
CheckSatResult
Sat -> forall a. SymVal a => SBV a -> Query a
getValue SString
badTopic