Skip to content

Instantly share code, notes, and snippets.

@nucular
Last active February 20, 2024 21:01
Show Gist options
  • Star 50 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save nucular/e19264af8d7fc8a26ece to your computer and use it in GitHub Desktop.
Save nucular/e19264af8d7fc8a26ece to your computer and use it in GitHub Desktop.
Omegle protocol reverse-engineering

Let's reverse-engineer Omegle. PROPERLY!

I could not find a proper, detailed (and up-to-date) reverse-engineerment of Omegle's text chat protocol on the internet, so here, have one made by analyzing the web app (web requests and source code).
The responses are beautified and the query strings split up and URI-decoded for readability.
Note that "query string" refers to parameters encoded into the URL and "form data" to parameters in the POST body which do not have to be URI-encoded.

TODO:

  • Find out how college authorization works
  • Find out how WebRTC video streaming works
  • Generally phrase things better

I hereby declare this document Public Domain. If you find it helpful for a project of yours, I only ask you to provide a link to it in your source so others interested can learn from it too.

Used Request Headers

Accept: application/json
Accept-Encoding: gzip,deflate
Accept-Language: en-US;q=0.6,en;q=0.4
Connection: keep-alive
DNT: 1
Host: front9.omegle.com
Origin: http://www.omegle.com
Referer: http://www.omegle.com/
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36

Note that the headers X-Requested-With and X-Request are deliberately removed from requests by the JavaScript.

Status

Pro-Tip: Fetch this first and then use one of the servers/subs listed for future connections (you can just connect to the main server here). You should switch to another one if your current one is not listed anymore.

GET http://[server].omegle.com/status

Query String

  • nocache = 0.4695589093025774 (optional, random nonce to prevent cached responses from being sent)
  • randid = CHPZGFFW (optional, see section "Start")

Response

{
    "count": 20840, // connection count, people say it's faked
    "force_unmon": true, // your IP was banned, see "Getting b&"
    "antinudeservers": [
        "waw3.omegle.com", "waw2.omegle.com", "waw1.omegle.com"
    ],
    "antinudepercent": 1.0,
    "spyQueueTime": 0.0, // if spyQueueTime is larger, there are more spies than
                         // spyees online, which the client uses to suggest a mode
    "spyeeQueueTime": 2.173300027847,
    "timestamp": 1409233880.7561221,
    "servers": [
        "front5.omegle.com", "front1.omegle.com", "front2.omegle.com",
        "front9.omegle.com", "front6.omegle.com", "front7.omegle.com",
        "front8.omegle.com", "front4.omegle.com", "front3.omegle.com"
    ]
}

Start

This actually starts the chat and gets us our client ID. If you want to start a question/spy chat, send wantsspy = 1 for spyee mode (answer a question) and ask = blah for spyer mode (ask a question).

POST http://[server].omegle.com/start

Query String

  • rcs = 1
  • firstevents = 1 (if 0 or not given, the response will not contain a statusInfo)
  • m = 1 (imitate a mobile connection, shouldn't really matter)
  • randid = CHPZGFFW (this is really just a random string containing 2-9 and A-Z, but excluding I and O)
  • spid = (some kind of ID from Adobe Stratus, never used though)
  • group = unmon (optional, join the unmonitored section instead)
  • lang = en (optional, two-char language code)
  • topics = ["foo", "bar", ...] (in default mode only)

Spyer/Spyee

  • wantsspy = 1 (for spyee mode)
  • ask = blah? (for spyer mode)
  • cansavequestion = 1 (optional, in spyer mode only, allow Omegle to reuse your question)

Camera Chat

  • webrtc = 1 (for camera chat)
  • camera = (probably just a flag, was " " for me in camera chat)

College Chat

  • college = ? (optional, TODO)
  • college_auth = ? (optional, TODO)
  • any_college = 1 (optional, TODO)

Response

{
    "events": [
        // see section "Events"
    ],
    "clientID": "central2:k0m4akq5ry4ytvklsrs2jmtsjkpbkh", // needed for all subsequent requests
    "statusInfo": {...} // see section "Status"
}

Events

This one uses long polling, which means that the server will be blocking the connection until an event happens. You can handle this with some async magic and long timeouts.

POST http://[server].omegle.com/events

Form Data

  • id = [clientID]

Response

[
    // see below
    ["event", "argument 1", ...],
    ["event", "argument 1", "argument 2", ...],
    // ...
]

Status events

  • ["waiting"] (the server is searching for strangers)
  • ["connected"] (you can start sending messages now)
  • ["statusInfo", {...}] (see section "Status")
  • ["count", 20900] (update the connection/online count, never encountered this one but it's in the source)

Notifications

  • ["commonLikes", ["foo", "bar", ...]] (the shared topics from the ones you've passed)
  • ["partnerCollege", "Foobar College"] (chat partner goes to this college)
  • ["serverMessage", "blah"] (most likely You both speak the same language.)
  • ["recaptchaRequired", "ChALlEnGe"] (see section "ReCAPTCHAs")
  • ["recaptchaRejected", "ChALlEnGe"]
  • ["identDigests", "a,b,c"] (probably only used for sharing logs)

Error events (disconnects you)

  • ["error", "blah!"] (general error message)
  • ["connectionDied"] (some technical error)
  • ["antinudeBanned"] (see section "Getting b8")

Chat events

  • ["typing"] (the stranger started typing)
  • ["stoppedTyping"] (...stopped typing)
  • ["gotMessage", "blah"] (...sent a message)
  • ["strangerDisconnected"] (...decided to disconnect)

In spyee mode

  • ["question", "blah?"] (the question you'll discuss with the stranger)

In spyer mode

  • ["question", "blah?"] (your question)
  • ["spyTyping", "Stranger <1/2>"] (Stranger 1/2 started typing)
  • ["spyStoppedTyping", "Stranger <1/2>"] (...stopped typing)
  • ["spyMessage", "Stranger <1/2>", "blah"] (...sent a message)
  • ["spyDisconnected", "Stranger <1/2>"] (...decided to disconnect)

In camera chat

  • ["icecandidate", "blah"] (see section "Connecting to the WebRTC peer")
  • ["rtccall", "blah"] (TODO)
  • ["rtcpeerdescription"] (see section "Send WebRTC Peer description")

Send messages

I'm not sure about the Unicode support, but it should work in most cases.

POST http://[server].omegle.com/send

Form Data

  • msg = lol
  • id = [clientID]

Response

win

Set your "typing" status

The server might detect clients that do not send these.

POST http://[server].omegle.com/typing
POST http://[server].omegle.com/stoppedtyping

Form Data

  • id = [clientID]

Response

win

Stop looking for common topics

If you've passed topics to /start, the server will send the waiting event and then search for people with the same topics until the client sends this. Use it after some time to stop the running search, ignore the topics and continue with connecting.

POST http://[server].omegle.com/stoplookingforcommonlikes

Form Data

  • id = [clientID]

Response

win

Disconnect from the current chat

Simple as that. Always use this to end sessions gracefully, unless another event already ended the session. This can also be used to disconnect both strangers in the spyer mode.

POST http://[server].omegle.com/disconnect

Form Data

  • id = [clientID]

Response

win

Send WebRTC Peer description

TODO

Send WebRTC ICE candidates

This is what you should do when your RTCPeerConnection fires the onicecandidate event. Basically, the Omegle client stores all received candidates in a list and then sets a 300ms timeout for more candidates to get pushed onto the list after which it sends the list to the server and clears it.

POST http://[server].omegle.com/icecandidate

Form Data

  • id = [clientID]
  • candidate = %7B...%7D (url-encoded JSON representation of a candidate)
  • candidate = ... (multiple times)

Response

win

Connecting to the WebRTC peer

TODO

ReCAPTCHAS

If you run into an recaptchaRequired event, you must prove that you are a human by fetching the captcha using the URL-encoded passed code from http://www.google.com/recaptcha/api/image?c=[challenge] and sending the answer like this. Note that I haven't tried this yet.

POST http://[server].omegle.com/recaptcha

Form Data

  • id = [clientID]
  • challenge = [challenge]
  • response = [answer]

Getting b&

If you got an antinudeBanned event, the modarating system banned you from the monitored section for "bad behaviour". From now on, the server status object (see section "Status") will have force_unmon set to true. You can switch to the unmonitored section by passing group = unmon to /start. They won't give you this mercy if you got your IP banned by connecting too rapidly or advertising, but bans don't last forever anyway.

Sharing logs

To upload your logs to Omegles server, use this.
Pro-Tip: You can actually pass any arbitrary text as the log. It's a JSON-encoded list containing lists that contain the strings. Oh, and HTML injection doesn't work, I tried.

These are the triggers that add some formatting:

  • ["*"] (smaller, bold font, gray)
  • ["* disconnected"] (as above)
  • ["Question to discuss:", "*"] (blue question box)
  • ["Stranger:", "*"] (large font, first item is red)
  • ["Stranger 1:", "*"] (as above)
  • ["Stranger 2:", "*"] (large font, first item is blue)
  • ["You:", "*"] (as above)
  • ["*", "*"] (normal font, first item is bold)

POST http://logs.omegle.com/generate

Form Data

  • log = [["You:", "blah"], ...] (JSON table of the plaintext chat, split in lines)
  • randid = CHPZGFFW (Your random ID)
  • topics = ["foo", "bar", ...] (optional, the shared topics)
  • identdigests = blah,blah,blah (the most recent data from the identDigests event)
  • host = 1

Response: 302 Found, the Location header contains the log link in the format http://logs.omegle.com/[id]. The log image can be fetched from http://l.omegle.com/[id].png.

@pwall2222
Copy link

@rosemash If thats true it would just make so much sense, I will be annotating that and playing with it, thanks!

@axelabbas
Copy link

@rosemash could you explain how did you find that please? Identifying the hash seems to be MD5 hash type, and when encoding my ip address it doesn't seem to match any of the hashes in identDigests

@rosemash
Copy link

rosemash commented Jan 16, 2023

@rosemash could you explain how did you find that please?

I inferred it by experimenting with different devices and looking at the behavior of the response. It still might not be true, but you can try for yourself, one of the hashes will always be the same as long as you're on the same network, and another will be the same unless you clear your browser's cookies/storage. When you connect to a new stranger, if they have a predictable opening, you can see the same behavior in their pair of hashes.

Identifying the hash seems to be MD5 hash type, and when encoding my ip address it doesn't seem to match any of the hashes in identDigests

It's almost definitely salted, there is no way to know what algorithm is being used.

@axelabbas
Copy link

@rosemash Oh I see, thanks a lot!

@Kyeya
Copy link

Kyeya commented Jan 23, 2023

@rosemash @axelabbas

For example it seems like: [hash1],[hash2],[hash3],[hash4] is actually two pairs, [user1hash1],[user1hash2],[user2hash1],[user2hash2] where [userXhash1] is IP address and [userXhash2] is a browser session token.

what you are calling userXhash2 is a hash of the randid passed to the start url, this will be some sort of hash of the ID you pass if it is a valid randid, anything else results in a default '2ac2f44c3ea3b8578fe95311b141a0b4' hash

userXhash1 is definitely related to the network you are on but if you connect to someone on the same network they will both have different hashes, userXhash2 will always be the same even if the other stranger has the same randid

you also now need to provide a 'cc' param to the start request which is the returned value from a post request to the "check" endpoint using one of the antinude servers subdomains you receive from querying the status endpoint

@axelabbas
Copy link

Omegle also disallows connection to the same stranger again after disconnecting for some time, using the userXhash2 (randId hash), So to meet the same stranger twice you'll have to change your randid value in the Start endpoint, and it'll allow it.

@bigwhitedog1
Copy link

how would I go about bypassing the omegle ban with this? Is there a way I can run javascript through console to unban myself? I'm new to coding so I have no idea.

@axelabbas
Copy link

@bigwhitedog1 you can't really do that.

@lppedd
Copy link

lppedd commented Sep 30, 2023

Thanks for the gist! And thanks everyone for the additional comments.
I've just used it to document my Kotlin implementation.
https://github.com/lppedd/komeglem

I'm missing the spyee and spyer modes, so if somebody wants to open PRs, they're welcomed.
Edit: seems they got removed lol

@Kyeya
Copy link

Kyeya commented Sep 30, 2023

@lppedd was just about to comment that they were disabled a few years ago

@lppedd
Copy link

lppedd commented Sep 30, 2023

@Kyeya yup read about it. I had left the implementation incomplete in 2021, and they got removed at the same time.

Anyway, it would be nice to have a mobile/desktop client with features like auto-skip, filters, censoring, etc. Currently, all the solutions involve injecting scripts into the webpage, which is still subpar.

@pwall2222
Copy link

pwall2222 commented Sep 30, 2023

@lppedd

Currently, all the solutions involve injecting scripts into the webpage, which is still subpar.

My project replaces the webpage entirely though and I don't think it'd be that hard to port to native, it's in typescript.
https://github.com/pwall2222/omclient
(also it's broken but just bcs /start params need to be updated)

@lppedd
Copy link

lppedd commented Oct 1, 2023

@pwall2222 thanks! Looks like you also fiddle with WebRTC, which is something I haven't looked at yet, since running it on a desktop app doesn't seem to be straightforward.

I'll try to give Compose Multiplatform a shot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment